mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
ip6tables: move ipv4-specific logic into its own file
A later change will introduce the equivalent IPv6 logic. #3549 PiperOrigin-RevId: 327499064
This commit is contained in:
committed by
gVisor bot
parent
5cf330106a
commit
182f66ee5e
@@ -6,6 +6,7 @@ go_library(
|
||||
name = "netfilter",
|
||||
srcs = [
|
||||
"extensions.go",
|
||||
"ipv4.go",
|
||||
"netfilter.go",
|
||||
"owner_matcher.go",
|
||||
"targets.go",
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package netfilter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/binary"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
// emptyIPv4Filter is for comparison with a rule's filters to determine whether
|
||||
// it is also empty. It is immutable.
|
||||
var emptyIPv4Filter = stack.IPHeaderFilter{
|
||||
Dst: "\x00\x00\x00\x00",
|
||||
DstMask: "\x00\x00\x00\x00",
|
||||
Src: "\x00\x00\x00\x00",
|
||||
SrcMask: "\x00\x00\x00\x00",
|
||||
}
|
||||
|
||||
func getEntries4(table stack.Table, info *linux.IPTGetinfo) linux.KernelIPTGetEntries {
|
||||
var entries linux.KernelIPTGetEntries
|
||||
copy(entries.Name[:], info.Name[:])
|
||||
|
||||
for ruleIdx, rule := range table.Rules {
|
||||
nflog("convert to binary: current offset: %d", entries.Size)
|
||||
|
||||
setHooksAndUnderflow(info, table, entries.Size, ruleIdx)
|
||||
// Each rule corresponds to an entry.
|
||||
entry := linux.KernelIPTEntry{
|
||||
Entry: linux.IPTEntry{
|
||||
IP: linux.IPTIP{
|
||||
Protocol: uint16(rule.Filter.Protocol),
|
||||
},
|
||||
NextOffset: linux.SizeOfIPTEntry,
|
||||
TargetOffset: linux.SizeOfIPTEntry,
|
||||
},
|
||||
}
|
||||
copy(entry.Entry.IP.Dst[:], rule.Filter.Dst)
|
||||
copy(entry.Entry.IP.DstMask[:], rule.Filter.DstMask)
|
||||
copy(entry.Entry.IP.Src[:], rule.Filter.Src)
|
||||
copy(entry.Entry.IP.SrcMask[:], rule.Filter.SrcMask)
|
||||
copy(entry.Entry.IP.OutputInterface[:], rule.Filter.OutputInterface)
|
||||
copy(entry.Entry.IP.OutputInterfaceMask[:], rule.Filter.OutputInterfaceMask)
|
||||
if rule.Filter.DstInvert {
|
||||
entry.Entry.IP.InverseFlags |= linux.IPT_INV_DSTIP
|
||||
}
|
||||
if rule.Filter.SrcInvert {
|
||||
entry.Entry.IP.InverseFlags |= linux.IPT_INV_SRCIP
|
||||
}
|
||||
if rule.Filter.OutputInterfaceInvert {
|
||||
entry.Entry.IP.InverseFlags |= linux.IPT_INV_VIA_OUT
|
||||
}
|
||||
|
||||
for _, matcher := range rule.Matchers {
|
||||
// Serialize the matcher and add it to the
|
||||
// entry.
|
||||
serialized := marshalMatcher(matcher)
|
||||
nflog("convert to binary: matcher serialized as: %v", serialized)
|
||||
if len(serialized)%8 != 0 {
|
||||
panic(fmt.Sprintf("matcher %T is not 64-bit aligned", matcher))
|
||||
}
|
||||
entry.Elems = append(entry.Elems, serialized...)
|
||||
entry.Entry.NextOffset += uint16(len(serialized))
|
||||
entry.Entry.TargetOffset += uint16(len(serialized))
|
||||
}
|
||||
|
||||
// Serialize and append the target.
|
||||
serialized := marshalTarget(rule.Target)
|
||||
if len(serialized)%8 != 0 {
|
||||
panic(fmt.Sprintf("target %T is not 64-bit aligned", rule.Target))
|
||||
}
|
||||
entry.Elems = append(entry.Elems, serialized...)
|
||||
entry.Entry.NextOffset += uint16(len(serialized))
|
||||
|
||||
nflog("convert to binary: adding entry: %+v", entry)
|
||||
|
||||
entries.Size += uint32(entry.Entry.NextOffset)
|
||||
entries.Entrytable = append(entries.Entrytable, entry)
|
||||
info.NumEntries++
|
||||
}
|
||||
|
||||
info.Size = entries.Size
|
||||
nflog("convert to binary: finished with an marshalled size of %d", info.Size)
|
||||
return entries
|
||||
}
|
||||
|
||||
func modifyEntries4(stk *stack.Stack, optVal []byte, replace *linux.IPTReplace, table *stack.Table) (map[uint32]int, *syserr.Error) {
|
||||
nflog("set entries: setting entries in table %q", replace.Name.String())
|
||||
|
||||
// Convert input into a list of rules and their offsets.
|
||||
var offset uint32
|
||||
// offsets maps rule byte offsets to their position in table.Rules.
|
||||
offsets := map[uint32]int{}
|
||||
for entryIdx := uint32(0); entryIdx < replace.NumEntries; entryIdx++ {
|
||||
nflog("set entries: processing entry at offset %d", offset)
|
||||
|
||||
// Get the struct ipt_entry.
|
||||
if len(optVal) < linux.SizeOfIPTEntry {
|
||||
nflog("optVal has insufficient size for entry %d", len(optVal))
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
var entry linux.IPTEntry
|
||||
buf := optVal[:linux.SizeOfIPTEntry]
|
||||
binary.Unmarshal(buf, usermem.ByteOrder, &entry)
|
||||
initialOptValLen := len(optVal)
|
||||
optVal = optVal[linux.SizeOfIPTEntry:]
|
||||
|
||||
if entry.TargetOffset < linux.SizeOfIPTEntry {
|
||||
nflog("entry has too-small target offset %d", entry.TargetOffset)
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/170): We should support more IPTIP
|
||||
// filtering fields.
|
||||
filter, err := filterFromIPTIP(entry.IP)
|
||||
if err != nil {
|
||||
nflog("bad iptip: %v", err)
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/170): Matchers and targets can specify
|
||||
// that they only work for certain protocols, hooks, tables.
|
||||
// Get matchers.
|
||||
matchersSize := entry.TargetOffset - linux.SizeOfIPTEntry
|
||||
if len(optVal) < int(matchersSize) {
|
||||
nflog("entry doesn't have enough room for its matchers (only %d bytes remain)", len(optVal))
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
matchers, err := parseMatchers(filter, optVal[:matchersSize])
|
||||
if err != nil {
|
||||
nflog("failed to parse matchers: %v", err)
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
optVal = optVal[matchersSize:]
|
||||
|
||||
// Get the target of the rule.
|
||||
targetSize := entry.NextOffset - entry.TargetOffset
|
||||
if len(optVal) < int(targetSize) {
|
||||
nflog("entry doesn't have enough room for its target (only %d bytes remain)", len(optVal))
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
target, err := parseTarget(filter, optVal[:targetSize])
|
||||
if err != nil {
|
||||
nflog("failed to parse target: %v", err)
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
optVal = optVal[targetSize:]
|
||||
|
||||
table.Rules = append(table.Rules, stack.Rule{
|
||||
Filter: filter,
|
||||
Target: target,
|
||||
Matchers: matchers,
|
||||
})
|
||||
offsets[offset] = int(entryIdx)
|
||||
offset += uint32(entry.NextOffset)
|
||||
|
||||
if initialOptValLen-len(optVal) != int(entry.NextOffset) {
|
||||
nflog("entry NextOffset is %d, but entry took up %d bytes", entry.NextOffset, initialOptValLen-len(optVal))
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
}
|
||||
return offsets, nil
|
||||
}
|
||||
|
||||
func filterFromIPTIP(iptip linux.IPTIP) (stack.IPHeaderFilter, error) {
|
||||
if containsUnsupportedFields4(iptip) {
|
||||
return stack.IPHeaderFilter{}, fmt.Errorf("unsupported fields in struct iptip: %+v", iptip)
|
||||
}
|
||||
if len(iptip.Dst) != header.IPv4AddressSize || len(iptip.DstMask) != header.IPv4AddressSize {
|
||||
return stack.IPHeaderFilter{}, fmt.Errorf("incorrect length of destination (%d) and/or destination mask (%d) fields", len(iptip.Dst), len(iptip.DstMask))
|
||||
}
|
||||
if len(iptip.Src) != header.IPv4AddressSize || len(iptip.SrcMask) != header.IPv4AddressSize {
|
||||
return stack.IPHeaderFilter{}, fmt.Errorf("incorrect length of source (%d) and/or source mask (%d) fields", len(iptip.Src), len(iptip.SrcMask))
|
||||
}
|
||||
|
||||
n := bytes.IndexByte([]byte(iptip.OutputInterface[:]), 0)
|
||||
if n == -1 {
|
||||
n = len(iptip.OutputInterface)
|
||||
}
|
||||
ifname := string(iptip.OutputInterface[:n])
|
||||
|
||||
n = bytes.IndexByte([]byte(iptip.OutputInterfaceMask[:]), 0)
|
||||
if n == -1 {
|
||||
n = len(iptip.OutputInterfaceMask)
|
||||
}
|
||||
ifnameMask := string(iptip.OutputInterfaceMask[:n])
|
||||
|
||||
return stack.IPHeaderFilter{
|
||||
Protocol: tcpip.TransportProtocolNumber(iptip.Protocol),
|
||||
Dst: tcpip.Address(iptip.Dst[:]),
|
||||
DstMask: tcpip.Address(iptip.DstMask[:]),
|
||||
DstInvert: iptip.InverseFlags&linux.IPT_INV_DSTIP != 0,
|
||||
Src: tcpip.Address(iptip.Src[:]),
|
||||
SrcMask: tcpip.Address(iptip.SrcMask[:]),
|
||||
SrcInvert: iptip.InverseFlags&linux.IPT_INV_SRCIP != 0,
|
||||
OutputInterface: ifname,
|
||||
OutputInterfaceMask: ifnameMask,
|
||||
OutputInterfaceInvert: iptip.InverseFlags&linux.IPT_INV_VIA_OUT != 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func containsUnsupportedFields4(iptip linux.IPTIP) bool {
|
||||
// The following features are supported:
|
||||
// - Protocol
|
||||
// - Dst and DstMask
|
||||
// - Src and SrcMask
|
||||
// - The inverse destination IP check flag
|
||||
// - OutputInterface, OutputInterfaceMask and its inverse.
|
||||
var emptyInterface = [linux.IFNAMSIZ]byte{}
|
||||
// Disable any supported inverse flags.
|
||||
inverseMask := uint8(linux.IPT_INV_DSTIP) | uint8(linux.IPT_INV_SRCIP) | uint8(linux.IPT_INV_VIA_OUT)
|
||||
return iptip.InputInterface != emptyInterface ||
|
||||
iptip.InputInterfaceMask != emptyInterface ||
|
||||
iptip.Flags != 0 ||
|
||||
iptip.InverseFlags&^inverseMask != 0
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package netfilter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
@@ -26,8 +25,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
@@ -37,15 +34,6 @@ import (
|
||||
// developing iptables, but can pollute sentry logs otherwise.
|
||||
const enableLogging = false
|
||||
|
||||
// emptyFilter is for comparison with a rule's filters to determine whether it
|
||||
// is also empty. It is immutable.
|
||||
var emptyFilter = stack.IPHeaderFilter{
|
||||
Dst: "\x00\x00\x00\x00",
|
||||
DstMask: "\x00\x00\x00\x00",
|
||||
Src: "\x00\x00\x00\x00",
|
||||
SrcMask: "\x00\x00\x00\x00",
|
||||
}
|
||||
|
||||
// nflog logs messages related to the writing and reading of iptables.
|
||||
func nflog(format string, args ...interface{}) {
|
||||
if enableLogging && log.IsLogging(log.Debug) {
|
||||
@@ -71,9 +59,9 @@ func GetInfo(t *kernel.Task, stack *stack.Stack, outPtr usermem.Addr) (linux.IPT
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// GetEntries returns netstack's iptables rules encoded for the iptables tool.
|
||||
func GetEntries(t *kernel.Task, stack *stack.Stack, outPtr usermem.Addr, outLen int) (linux.KernelIPTGetEntries, *syserr.Error) {
|
||||
// Read in the struct and table name.
|
||||
// GetEntries4 returns netstack's iptables rules encoded for the iptables tool.
|
||||
func GetEntries4(t *kernel.Task, stack *stack.Stack, outPtr usermem.Addr, outLen int) (linux.KernelIPTGetEntries, *syserr.Error) {
|
||||
// Read in the ABI struct.
|
||||
var userEntries linux.IPTGetEntries
|
||||
if _, err := userEntries.CopyIn(t, outPtr); err != nil {
|
||||
nflog("couldn't copy in entries %q", userEntries.Name)
|
||||
@@ -99,108 +87,48 @@ func GetEntries(t *kernel.Task, stack *stack.Stack, outPtr usermem.Addr, outLen
|
||||
// format expected by the iptables tool. Linux stores each table as a binary
|
||||
// blob that can only be traversed by parsing a bit, reading some offsets,
|
||||
// jumping to those offsets, parsing again, etc.
|
||||
func convertNetstackToBinary(stack *stack.Stack, tablename linux.TableName) (linux.KernelIPTGetEntries, linux.IPTGetinfo, error) {
|
||||
table, ok := stack.IPTables().GetTable(tablename.String())
|
||||
if !ok {
|
||||
return linux.KernelIPTGetEntries{}, linux.IPTGetinfo{}, fmt.Errorf("couldn't find table %q", tablename)
|
||||
}
|
||||
|
||||
var entries linux.KernelIPTGetEntries
|
||||
var info linux.IPTGetinfo
|
||||
info.ValidHooks = table.ValidHooks()
|
||||
|
||||
func convertNetstackToBinary(stk *stack.Stack, tablename linux.TableName) (linux.KernelIPTGetEntries, linux.IPTGetinfo, error) {
|
||||
// The table name has to fit in the struct.
|
||||
if linux.XT_TABLE_MAXNAMELEN < len(tablename) {
|
||||
return linux.KernelIPTGetEntries{}, linux.IPTGetinfo{}, fmt.Errorf("table name %q too long", tablename)
|
||||
}
|
||||
copy(info.Name[:], tablename[:])
|
||||
copy(entries.Name[:], tablename[:])
|
||||
|
||||
for ruleIdx, rule := range table.Rules {
|
||||
nflog("convert to binary: current offset: %d", entries.Size)
|
||||
|
||||
// Is this a chain entry point?
|
||||
for hook, hookRuleIdx := range table.BuiltinChains {
|
||||
if hookRuleIdx == ruleIdx {
|
||||
nflog("convert to binary: found hook %d at offset %d", hook, entries.Size)
|
||||
info.HookEntry[hook] = entries.Size
|
||||
}
|
||||
}
|
||||
// Is this a chain underflow point?
|
||||
for underflow, underflowRuleIdx := range table.Underflows {
|
||||
if underflowRuleIdx == ruleIdx {
|
||||
nflog("convert to binary: found underflow %d at offset %d", underflow, entries.Size)
|
||||
info.Underflow[underflow] = entries.Size
|
||||
}
|
||||
}
|
||||
|
||||
// Each rule corresponds to an entry.
|
||||
entry := linux.KernelIPTEntry{
|
||||
Entry: linux.IPTEntry{
|
||||
IP: linux.IPTIP{
|
||||
Protocol: uint16(rule.Filter.Protocol),
|
||||
},
|
||||
NextOffset: linux.SizeOfIPTEntry,
|
||||
TargetOffset: linux.SizeOfIPTEntry,
|
||||
},
|
||||
}
|
||||
copy(entry.Entry.IP.Dst[:], rule.Filter.Dst)
|
||||
copy(entry.Entry.IP.DstMask[:], rule.Filter.DstMask)
|
||||
copy(entry.Entry.IP.Src[:], rule.Filter.Src)
|
||||
copy(entry.Entry.IP.SrcMask[:], rule.Filter.SrcMask)
|
||||
copy(entry.Entry.IP.OutputInterface[:], rule.Filter.OutputInterface)
|
||||
copy(entry.Entry.IP.OutputInterfaceMask[:], rule.Filter.OutputInterfaceMask)
|
||||
if rule.Filter.DstInvert {
|
||||
entry.Entry.IP.InverseFlags |= linux.IPT_INV_DSTIP
|
||||
}
|
||||
if rule.Filter.SrcInvert {
|
||||
entry.Entry.IP.InverseFlags |= linux.IPT_INV_SRCIP
|
||||
}
|
||||
if rule.Filter.OutputInterfaceInvert {
|
||||
entry.Entry.IP.InverseFlags |= linux.IPT_INV_VIA_OUT
|
||||
}
|
||||
|
||||
for _, matcher := range rule.Matchers {
|
||||
// Serialize the matcher and add it to the
|
||||
// entry.
|
||||
serialized := marshalMatcher(matcher)
|
||||
nflog("convert to binary: matcher serialized as: %v", serialized)
|
||||
if len(serialized)%8 != 0 {
|
||||
panic(fmt.Sprintf("matcher %T is not 64-bit aligned", matcher))
|
||||
}
|
||||
entry.Elems = append(entry.Elems, serialized...)
|
||||
entry.Entry.NextOffset += uint16(len(serialized))
|
||||
entry.Entry.TargetOffset += uint16(len(serialized))
|
||||
}
|
||||
|
||||
// Serialize and append the target.
|
||||
serialized := marshalTarget(rule.Target)
|
||||
if len(serialized)%8 != 0 {
|
||||
panic(fmt.Sprintf("target %T is not 64-bit aligned", rule.Target))
|
||||
}
|
||||
entry.Elems = append(entry.Elems, serialized...)
|
||||
entry.Entry.NextOffset += uint16(len(serialized))
|
||||
|
||||
nflog("convert to binary: adding entry: %+v", entry)
|
||||
|
||||
entries.Size += uint32(entry.Entry.NextOffset)
|
||||
entries.Entrytable = append(entries.Entrytable, entry)
|
||||
info.NumEntries++
|
||||
table, ok := stk.IPTables().GetTable(tablename.String())
|
||||
if !ok {
|
||||
return linux.KernelIPTGetEntries{}, linux.IPTGetinfo{}, fmt.Errorf("couldn't find table %q", tablename)
|
||||
}
|
||||
|
||||
nflog("convert to binary: finished with an marshalled size of %d", info.Size)
|
||||
info.Size = entries.Size
|
||||
// Setup the info struct.
|
||||
var info linux.IPTGetinfo
|
||||
info.ValidHooks = table.ValidHooks()
|
||||
copy(info.Name[:], tablename[:])
|
||||
|
||||
entries := getEntries4(table, &info)
|
||||
return entries, info, nil
|
||||
}
|
||||
|
||||
// setHooksAndUnderflow checks whether the rule at ruleIdx is a hook entrypoint
|
||||
// or underflow, in which case it fills in info.HookEntry and info.Underflows.
|
||||
func setHooksAndUnderflow(info *linux.IPTGetinfo, table stack.Table, offset uint32, ruleIdx int) {
|
||||
// Is this a chain entry point?
|
||||
for hook, hookRuleIdx := range table.BuiltinChains {
|
||||
if hookRuleIdx == ruleIdx {
|
||||
nflog("convert to binary: found hook %d at offset %d", hook, offset)
|
||||
info.HookEntry[hook] = offset
|
||||
}
|
||||
}
|
||||
// Is this a chain underflow point?
|
||||
for underflow, underflowRuleIdx := range table.Underflows {
|
||||
if underflowRuleIdx == ruleIdx {
|
||||
nflog("convert to binary: found underflow %d at offset %d", underflow, offset)
|
||||
info.Underflow[underflow] = offset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetEntries sets iptables rules for a single table. See
|
||||
// net/ipv4/netfilter/ip_tables.c:translate_table for reference.
|
||||
func SetEntries(stk *stack.Stack, optVal []byte) *syserr.Error {
|
||||
// Get the basic rules data (struct ipt_replace).
|
||||
if len(optVal) < linux.SizeOfIPTReplace {
|
||||
nflog("optVal has insufficient size for replace %d", len(optVal))
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
var replace linux.IPTReplace
|
||||
replaceBuf := optVal[:linux.SizeOfIPTReplace]
|
||||
optVal = optVal[linux.SizeOfIPTReplace:]
|
||||
@@ -218,79 +146,9 @@ func SetEntries(stk *stack.Stack, optVal []byte) *syserr.Error {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
nflog("set entries: setting entries in table %q", replace.Name.String())
|
||||
|
||||
// Convert input into a list of rules and their offsets.
|
||||
var offset uint32
|
||||
// offsets maps rule byte offsets to their position in table.Rules.
|
||||
offsets := map[uint32]int{}
|
||||
for entryIdx := uint32(0); entryIdx < replace.NumEntries; entryIdx++ {
|
||||
nflog("set entries: processing entry at offset %d", offset)
|
||||
|
||||
// Get the struct ipt_entry.
|
||||
if len(optVal) < linux.SizeOfIPTEntry {
|
||||
nflog("optVal has insufficient size for entry %d", len(optVal))
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
var entry linux.IPTEntry
|
||||
buf := optVal[:linux.SizeOfIPTEntry]
|
||||
binary.Unmarshal(buf, usermem.ByteOrder, &entry)
|
||||
initialOptValLen := len(optVal)
|
||||
optVal = optVal[linux.SizeOfIPTEntry:]
|
||||
|
||||
if entry.TargetOffset < linux.SizeOfIPTEntry {
|
||||
nflog("entry has too-small target offset %d", entry.TargetOffset)
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/170): We should support more IPTIP
|
||||
// filtering fields.
|
||||
filter, err := filterFromIPTIP(entry.IP)
|
||||
if err != nil {
|
||||
nflog("bad iptip: %v", err)
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/170): Matchers and targets can specify
|
||||
// that they only work for certain protocols, hooks, tables.
|
||||
// Get matchers.
|
||||
matchersSize := entry.TargetOffset - linux.SizeOfIPTEntry
|
||||
if len(optVal) < int(matchersSize) {
|
||||
nflog("entry doesn't have enough room for its matchers (only %d bytes remain)", len(optVal))
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
matchers, err := parseMatchers(filter, optVal[:matchersSize])
|
||||
if err != nil {
|
||||
nflog("failed to parse matchers: %v", err)
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
optVal = optVal[matchersSize:]
|
||||
|
||||
// Get the target of the rule.
|
||||
targetSize := entry.NextOffset - entry.TargetOffset
|
||||
if len(optVal) < int(targetSize) {
|
||||
nflog("entry doesn't have enough room for its target (only %d bytes remain)", len(optVal))
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
target, err := parseTarget(filter, optVal[:targetSize])
|
||||
if err != nil {
|
||||
nflog("failed to parse target: %v", err)
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
optVal = optVal[targetSize:]
|
||||
|
||||
table.Rules = append(table.Rules, stack.Rule{
|
||||
Filter: filter,
|
||||
Target: target,
|
||||
Matchers: matchers,
|
||||
})
|
||||
offsets[offset] = int(entryIdx)
|
||||
offset += uint32(entry.NextOffset)
|
||||
|
||||
if initialOptValLen-len(optVal) != int(entry.NextOffset) {
|
||||
nflog("entry NextOffset is %d, but entry took up %d bytes", entry.NextOffset, initialOptValLen-len(optVal))
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
offsets, err := modifyEntries4(stk, optVal, &replace, &table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Go through the list of supported hooks for this table and, for each
|
||||
@@ -323,7 +181,7 @@ func SetEntries(stk *stack.Stack, optVal []byte) *syserr.Error {
|
||||
}
|
||||
}
|
||||
|
||||
// Add the user chains.
|
||||
// Check the user chains.
|
||||
for ruleIdx, rule := range table.Rules {
|
||||
if _, ok := rule.Target.(stack.UserChainTarget); !ok {
|
||||
continue
|
||||
@@ -404,7 +262,6 @@ func parseMatchers(filter stack.IPHeaderFilter, optVal []byte) ([]stack.Matcher,
|
||||
|
||||
// Check some invariants.
|
||||
if match.MatchSize < linux.SizeOfXTEntryMatch {
|
||||
|
||||
return nil, fmt.Errorf("match size is too small, must be at least %d", linux.SizeOfXTEntryMatch)
|
||||
}
|
||||
if len(optVal) < int(match.MatchSize) {
|
||||
@@ -429,64 +286,11 @@ func parseMatchers(filter stack.IPHeaderFilter, optVal []byte) ([]stack.Matcher,
|
||||
return matchers, nil
|
||||
}
|
||||
|
||||
func filterFromIPTIP(iptip linux.IPTIP) (stack.IPHeaderFilter, error) {
|
||||
if containsUnsupportedFields(iptip) {
|
||||
return stack.IPHeaderFilter{}, fmt.Errorf("unsupported fields in struct iptip: %+v", iptip)
|
||||
}
|
||||
if len(iptip.Dst) != header.IPv4AddressSize || len(iptip.DstMask) != header.IPv4AddressSize {
|
||||
return stack.IPHeaderFilter{}, fmt.Errorf("incorrect length of destination (%d) and/or destination mask (%d) fields", len(iptip.Dst), len(iptip.DstMask))
|
||||
}
|
||||
if len(iptip.Src) != header.IPv4AddressSize || len(iptip.SrcMask) != header.IPv4AddressSize {
|
||||
return stack.IPHeaderFilter{}, fmt.Errorf("incorrect length of source (%d) and/or source mask (%d) fields", len(iptip.Src), len(iptip.SrcMask))
|
||||
}
|
||||
|
||||
n := bytes.IndexByte([]byte(iptip.OutputInterface[:]), 0)
|
||||
if n == -1 {
|
||||
n = len(iptip.OutputInterface)
|
||||
}
|
||||
ifname := string(iptip.OutputInterface[:n])
|
||||
|
||||
n = bytes.IndexByte([]byte(iptip.OutputInterfaceMask[:]), 0)
|
||||
if n == -1 {
|
||||
n = len(iptip.OutputInterfaceMask)
|
||||
}
|
||||
ifnameMask := string(iptip.OutputInterfaceMask[:n])
|
||||
|
||||
return stack.IPHeaderFilter{
|
||||
Protocol: tcpip.TransportProtocolNumber(iptip.Protocol),
|
||||
Dst: tcpip.Address(iptip.Dst[:]),
|
||||
DstMask: tcpip.Address(iptip.DstMask[:]),
|
||||
DstInvert: iptip.InverseFlags&linux.IPT_INV_DSTIP != 0,
|
||||
Src: tcpip.Address(iptip.Src[:]),
|
||||
SrcMask: tcpip.Address(iptip.SrcMask[:]),
|
||||
SrcInvert: iptip.InverseFlags&linux.IPT_INV_SRCIP != 0,
|
||||
OutputInterface: ifname,
|
||||
OutputInterfaceMask: ifnameMask,
|
||||
OutputInterfaceInvert: iptip.InverseFlags&linux.IPT_INV_VIA_OUT != 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func containsUnsupportedFields(iptip linux.IPTIP) bool {
|
||||
// The following features are supported:
|
||||
// - Protocol
|
||||
// - Dst and DstMask
|
||||
// - Src and SrcMask
|
||||
// - The inverse destination IP check flag
|
||||
// - OutputInterface, OutputInterfaceMask and its inverse.
|
||||
var emptyInterface = [linux.IFNAMSIZ]byte{}
|
||||
// Disable any supported inverse flags.
|
||||
inverseMask := uint8(linux.IPT_INV_DSTIP) | uint8(linux.IPT_INV_SRCIP) | uint8(linux.IPT_INV_VIA_OUT)
|
||||
return iptip.InputInterface != emptyInterface ||
|
||||
iptip.InputInterfaceMask != emptyInterface ||
|
||||
iptip.Flags != 0 ||
|
||||
iptip.InverseFlags&^inverseMask != 0
|
||||
}
|
||||
|
||||
func validUnderflow(rule stack.Rule) bool {
|
||||
if len(rule.Matchers) != 0 {
|
||||
return false
|
||||
}
|
||||
if rule.Filter != emptyFilter {
|
||||
if rule.Filter != emptyIPv4Filter {
|
||||
return false
|
||||
}
|
||||
switch rule.Target.(type) {
|
||||
|
||||
@@ -949,6 +949,9 @@ func (s *SocketOperations) GetSockOpt(t *kernel.Task, level, name int, outPtr us
|
||||
if outLen < linux.SizeOfIPTGetinfo {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
if s.family != linux.AF_INET {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
stack := inet.StackFromContext(t)
|
||||
if stack == nil {
|
||||
@@ -964,12 +967,15 @@ func (s *SocketOperations) GetSockOpt(t *kernel.Task, level, name int, outPtr us
|
||||
if outLen < linux.SizeOfIPTGetEntries {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
if s.family != linux.AF_INET {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
stack := inet.StackFromContext(t)
|
||||
if stack == nil {
|
||||
return nil, syserr.ErrNoDevice
|
||||
}
|
||||
entries, err := netfilter.GetEntries(t, stack.(*Stack).Stack, outPtr, outLen)
|
||||
entries, err := netfilter.GetEntries4(t, stack.(*Stack).Stack, outPtr, outLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1650,12 +1656,15 @@ func (s *SocketOperations) SetSockOpt(t *kernel.Task, level int, name int, optVa
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.skType == linux.SOCK_RAW && level == linux.IPPROTO_IP {
|
||||
if s.skType == linux.SOCK_RAW && level == linux.SOL_IP {
|
||||
switch name {
|
||||
case linux.IPT_SO_SET_REPLACE:
|
||||
if len(optVal) < linux.SizeOfIPTReplace {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
if s.family != linux.AF_INET {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
stack := inet.StackFromContext(t)
|
||||
if stack == nil {
|
||||
|
||||
@@ -239,6 +239,9 @@ func (s *SocketVFS2) GetSockOpt(t *kernel.Task, level, name int, outPtr usermem.
|
||||
if outLen < linux.SizeOfIPTGetinfo {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
if s.family != linux.AF_INET {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
stack := inet.StackFromContext(t)
|
||||
if stack == nil {
|
||||
@@ -254,12 +257,15 @@ func (s *SocketVFS2) GetSockOpt(t *kernel.Task, level, name int, outPtr usermem.
|
||||
if outLen < linux.SizeOfIPTGetEntries {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
if s.family != linux.AF_INET {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
stack := inet.StackFromContext(t)
|
||||
if stack == nil {
|
||||
return nil, syserr.ErrNoDevice
|
||||
}
|
||||
entries, err := netfilter.GetEntries(t, stack.(*Stack).Stack, outPtr, outLen)
|
||||
entries, err := netfilter.GetEntries4(t, stack.(*Stack).Stack, outPtr, outLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -298,12 +304,15 @@ func (s *SocketVFS2) SetSockOpt(t *kernel.Task, level int, name int, optVal []by
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.skType == linux.SOCK_RAW && level == linux.IPPROTO_IP {
|
||||
if s.skType == linux.SOCK_RAW && level == linux.SOL_IP {
|
||||
switch name {
|
||||
case linux.IPT_SO_SET_REPLACE:
|
||||
if len(optVal) < linux.SizeOfIPTReplace {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
if s.family != linux.AF_INET {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
stack := inet.StackFromContext(t)
|
||||
if stack == nil {
|
||||
|
||||
@@ -632,6 +632,8 @@ var sockOptNames = map[uint64]abi.ValueSet{
|
||||
linux.IPV6_UNICAST_IF: "IPV6_UNICAST_IF",
|
||||
linux.MCAST_MSFILTER: "MCAST_MSFILTER",
|
||||
linux.IPV6_ADDRFORM: "IPV6_ADDRFORM",
|
||||
linux.IP6T_SO_GET_INFO: "IP6T_SO_GET_INFO",
|
||||
linux.IP6T_SO_GET_ENTRIES: "IP6T_SO_GET_ENTRIES",
|
||||
},
|
||||
linux.SOL_NETLINK: {
|
||||
linux.NETLINK_BROADCAST_ERROR: "NETLINK_BROADCAST_ERROR",
|
||||
|
||||
Reference in New Issue
Block a user