summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/abi/linux/netfilter.go23
-rw-r--r--pkg/sentry/socket/netfilter/netfilter.go33
-rw-r--r--pkg/tcpip/iptables/iptables.go10
-rw-r--r--pkg/tcpip/iptables/targets.go66
-rw-r--r--pkg/tcpip/iptables/types.go4
-rw-r--r--pkg/tcpip/stack/nic.go13
-rw-r--r--test/iptables/nat.go12
7 files changed, 103 insertions, 58 deletions
diff --git a/pkg/abi/linux/netfilter.go b/pkg/abi/linux/netfilter.go
index ba4d84962..2179ac995 100644
--- a/pkg/abi/linux/netfilter.go
+++ b/pkg/abi/linux/netfilter.go
@@ -250,8 +250,24 @@ type XTErrorTarget struct {
// SizeOfXTErrorTarget is the size of an XTErrorTarget.
const SizeOfXTErrorTarget = 64
+// Flag values for NfNATIPV4Range. The values indicate whether to map
+// protocol specific part(ports) or IPs. It corresponds to values in
+// include/uapi/linux/netfilter/nf_nat.h.
+const (
+ NF_NAT_RANGE_MAP_IPS = 1 << 0
+ NF_NAT_RANGE_PROTO_SPECIFIED = 1 << 1
+ NF_NAT_RANGE_PROTO_RANDOM = 1 << 2
+ NF_NAT_RANGE_PERSISTENT = 1 << 3
+ NF_NAT_RANGE_PROTO_RANDOM_FULLY = 1 << 4
+ NF_NAT_RANGE_PROTO_RANDOM_ALL = (NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
+ NF_NAT_RANGE_MASK = (NF_NAT_RANGE_MAP_IPS |
+ NF_NAT_RANGE_PROTO_SPECIFIED | NF_NAT_RANGE_PROTO_RANDOM |
+ NF_NAT_RANGE_PERSISTENT | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
+)
+
// NfNATIPV4Range. It corresponds to struct nf_nat_ipv4_range
-// in include/uapi/linux/netfilter/nf_nat.h.
+// in include/uapi/linux/netfilter/nf_nat.h. The fields are in
+// network byte order.
type NfNATIPV4Range struct {
Flags uint32
MinIP [4]byte
@@ -263,11 +279,12 @@ type NfNATIPV4Range struct {
// NfNATIPV4MultiRangeCompat. It corresponds to struct
// nf_nat_ipv4_multi_range_compat in include/uapi/linux/netfilter/nf_nat.h.
type NfNATIPV4MultiRangeCompat struct {
- Rangesize uint32
- RangeIPV4 [1]NfNATIPV4Range
+ RangeSize uint32
+ RangeIPV4 NfNATIPV4Range
}
// XTRedirectTarget triggers a redirect when reached.
+// Adding 4 bytes of padding to make the struct 8 byte aligned.
type XTRedirectTarget struct {
Target XTEntryTarget
NfRange NfNATIPV4MultiRangeCompat
diff --git a/pkg/sentry/socket/netfilter/netfilter.go b/pkg/sentry/socket/netfilter/netfilter.go
index 512ad624a..257cb485b 100644
--- a/pkg/sentry/socket/netfilter/netfilter.go
+++ b/pkg/sentry/socket/netfilter/netfilter.go
@@ -26,6 +26,7 @@ import (
"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/iptables"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/usermem"
@@ -288,6 +289,7 @@ func marshalRedirectTarget() []byte {
TargetSize: linux.SizeOfXTRedirectTarget,
},
}
+ copy(target.Target.Name[:], redirectTargetName)
ret := make([]byte, 0, linux.SizeOfXTRedirectTarget)
return binary.Marshal(ret, usermem.ByteOrder, target)
@@ -405,7 +407,7 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
nflog("entry doesn't have enough room for its target (only %d bytes remain)", len(optVal))
return syserr.ErrInvalidArgument
}
- target, err := parseTarget(optVal[:targetSize])
+ target, err := parseTarget(filter, optVal[:targetSize])
if err != nil {
nflog("failed to parse target: %v", err)
return syserr.ErrInvalidArgument
@@ -552,7 +554,7 @@ func parseMatchers(filter iptables.IPHeaderFilter, optVal []byte) ([]iptables.Ma
// parseTarget parses a target from optVal. optVal should contain only the
// target.
-func parseTarget(optVal []byte) (iptables.Target, error) {
+func parseTarget(filter iptables.IPHeaderFilter, optVal []byte) (iptables.Target, error) {
nflog("set entries: parsing target of size %d", len(optVal))
if len(optVal) < linux.SizeOfXTEntryTarget {
return nil, fmt.Errorf("optVal has insufficient size for entry target %d", len(optVal))
@@ -604,6 +606,10 @@ func parseTarget(optVal []byte) (iptables.Target, error) {
return nil, fmt.Errorf("netfilter.SetEntries: optVal has insufficient size for redirect target %d", len(optVal))
}
+ if filter.Protocol != header.TCPProtocolNumber && filter.Protocol != header.UDPProtocolNumber {
+ return nil, fmt.Errorf("netfilter.SetEntries: invalid argument")
+ }
+
var redirectTarget linux.XTRedirectTarget
buf = optVal[:linux.SizeOfXTRedirectTarget]
binary.Unmarshal(buf, usermem.ByteOrder, &redirectTarget)
@@ -612,21 +618,30 @@ func parseTarget(optVal []byte) (iptables.Target, error) {
var target iptables.RedirectTarget
nfRange := redirectTarget.NfRange
- target.RangeSize = nfRange.Rangesize
- target.Flags = nfRange.RangeIPV4[0].Flags
+ // RangeSize should be 1.
+ if nfRange.RangeSize != 1 {
+ return nil, fmt.Errorf("netfilter.SetEntries: invalid argument")
+ }
+
+ // TODO(gvisor.dev/issue/170): Check if the flags are valid.
+ // Also check if we need to map ports or IP.
+ // For now, redirect target only supports dest port change.
+ if nfRange.RangeIPV4.Flags&linux.NF_NAT_RANGE_PROTO_SPECIFIED == 0 {
+ return nil, fmt.Errorf("netfilter.SetEntries: invalid argument.")
+ }
+ target.Flags = nfRange.RangeIPV4.Flags
- target.MinIP = tcpip.Address(nfRange.RangeIPV4[0].MinIP[:])
- target.MaxIP = tcpip.Address(nfRange.RangeIPV4[0].MaxIP[:])
+ target.MinIP = tcpip.Address(nfRange.RangeIPV4.MinIP[:])
+ target.MaxIP = tcpip.Address(nfRange.RangeIPV4.MaxIP[:])
// Convert port from big endian to little endian.
port := make([]byte, 2)
- binary.BigEndian.PutUint16(port, nfRange.RangeIPV4[0].MinPort)
+ binary.BigEndian.PutUint16(port, nfRange.RangeIPV4.MinPort)
target.MinPort = binary.LittleEndian.Uint16(port)
- binary.BigEndian.PutUint16(port, nfRange.RangeIPV4[0].MaxPort)
+ binary.BigEndian.PutUint16(port, nfRange.RangeIPV4.MaxPort)
target.MaxPort = binary.LittleEndian.Uint16(port)
return target, nil
-
}
// Unknown target.
diff --git a/pkg/tcpip/iptables/iptables.go b/pkg/tcpip/iptables/iptables.go
index c00d012c0..f7dc4f720 100644
--- a/pkg/tcpip/iptables/iptables.go
+++ b/pkg/tcpip/iptables/iptables.go
@@ -207,7 +207,7 @@ func (it *IPTables) checkTable(hook Hook, pkt tcpip.PacketBuffer, tablename stri
underflow := table.Rules[table.Underflows[hook]]
// Underflow is guaranteed to be an unconditional
// ACCEPT or DROP.
- switch v, _ := underflow.Target.Action(pkt); v {
+ switch v, _ := underflow.Target.Action(pkt, underflow.Filter); v {
case RuleAccept:
return TableAccept
case RuleDrop:
@@ -233,6 +233,12 @@ func (it *IPTables) checkTable(hook Hook, pkt tcpip.PacketBuffer, tablename stri
func (it *IPTables) checkRule(hook Hook, pkt tcpip.PacketBuffer, table Table, ruleIdx int) RuleVerdict {
rule := table.Rules[ruleIdx]
+ // If pkt.NetworkHeader hasn't been set yet, it will be contained in
+ // pkt.Data.First().
+ if pkt.NetworkHeader == nil {
+ pkt.NetworkHeader = pkt.Data.First()
+ }
+
// First check whether the packet matches the IP header filter.
// TODO(gvisor.dev/issue/170): Support other fields of the filter.
if rule.Filter.Protocol != 0 && rule.Filter.Protocol != header.IPv4(pkt.NetworkHeader).TransportProtocol() {
@@ -252,6 +258,6 @@ func (it *IPTables) checkRule(hook Hook, pkt tcpip.PacketBuffer, table Table, ru
}
// All the matchers matched, so run the target.
- verdict, _ := rule.Target.Action(pkt)
+ verdict, _ := rule.Target.Action(pkt, rule.Filter)
return verdict
}
diff --git a/pkg/tcpip/iptables/targets.go b/pkg/tcpip/iptables/targets.go
index 06e65bece..a75938da3 100644
--- a/pkg/tcpip/iptables/targets.go
+++ b/pkg/tcpip/iptables/targets.go
@@ -26,7 +26,7 @@ import (
type AcceptTarget struct{}
// Action implements Target.Action.
-func (AcceptTarget) Action(packet tcpip.PacketBuffer) (RuleVerdict, string) {
+func (AcceptTarget) Action(packet tcpip.PacketBuffer, filter IPHeaderFilter) (RuleVerdict, string) {
return RuleAccept, ""
}
@@ -34,7 +34,7 @@ func (AcceptTarget) Action(packet tcpip.PacketBuffer) (RuleVerdict, string) {
type DropTarget struct{}
// Action implements Target.Action.
-func (DropTarget) Action(packet tcpip.PacketBuffer) (RuleVerdict, string) {
+func (DropTarget) Action(packet tcpip.PacketBuffer, filter IPHeaderFilter) (RuleVerdict, string) {
return RuleDrop, ""
}
@@ -43,7 +43,7 @@ func (DropTarget) Action(packet tcpip.PacketBuffer) (RuleVerdict, string) {
type ErrorTarget struct{}
// Action implements Target.Action.
-func (ErrorTarget) Action(packet tcpip.PacketBuffer) (RuleVerdict, string) {
+func (ErrorTarget) Action(packet tcpip.PacketBuffer, filter IPHeaderFilter) (RuleVerdict, string) {
log.Debugf("ErrorTarget triggered.")
return RuleDrop, ""
}
@@ -54,7 +54,7 @@ type UserChainTarget struct {
}
// Action implements Target.Action.
-func (UserChainTarget) Action(tcpip.PacketBuffer) (RuleVerdict, string) {
+func (UserChainTarget) Action(tcpip.PacketBuffer, IPHeaderFilter) (RuleVerdict, string) {
panic("UserChainTarget should never be called.")
}
@@ -63,29 +63,55 @@ func (UserChainTarget) Action(tcpip.PacketBuffer) (RuleVerdict, string) {
type ReturnTarget struct{}
// Action implements Target.Action.
-func (ReturnTarget) Action(tcpip.PacketBuffer) (RuleVerdict, string) {
+func (ReturnTarget) Action(tcpip.PacketBuffer, IPHeaderFilter) (RuleVerdict, string) {
return RuleReturn, ""
}
// RedirectTarget redirects the packet by modifying the destination port/IP.
+// Min and Max values for IP and Ports in the struct indicate the range of
+// values which can be used to redirect.
type RedirectTarget struct {
- RangeSize uint32
- Flags uint32
- MinIP tcpip.Address
- MaxIP tcpip.Address
- MinPort uint16
- MaxPort uint16
-}
+ // Flags to check if the redirect is for address or ports.
+ Flags uint32
-// Action implements Target.Action.
-func (rt RedirectTarget) Action(packet tcpip.PacketBuffer) (RuleVerdict, string) {
- log.Infof("RedirectTarget triggered.")
+ // Min address used to redirect.
+ MinIP tcpip.Address
+
+ // Max address used to redirect.
+ MaxIP tcpip.Address
- // TODO(gvisor.dev/issue/170): Checking only for UDP protocol.
- // We're yet to support for TCP protocol.
- headerView := packet.Data.First()
- h := header.UDP(headerView)
- h.SetDestinationPort(rt.MinPort)
+ // Min port used to redirect.
+ MinPort uint16
+ // Max port used to redirect.
+ MaxPort uint16
+}
+
+// Action implements Target.Action.
+func (rt RedirectTarget) Action(pkt tcpip.PacketBuffer, filter IPHeaderFilter) (RuleVerdict, string) {
+ headerView := pkt.Data.First()
+
+ // Network header should be set.
+ netHeader := header.IPv4(headerView)
+ if netHeader == nil {
+ return RuleDrop, ""
+ }
+
+ // TODO(gvisor.dev/issue/170): Check Flags in RedirectTarget if
+ // we need to change dest address (for OUTPUT chain) or ports.
+ hlen := int(netHeader.HeaderLength())
+
+ switch protocol := filter.Protocol; protocol {
+ case header.UDPProtocolNumber:
+ udp := header.UDP(headerView[hlen:])
+ udp.SetDestinationPort(rt.MinPort)
+ case header.TCPProtocolNumber:
+ // TODO(gvisor.dev/issue/170): Need to recompute checksum
+ // and implement nat connection tracking to support TCP.
+ tcp := header.TCP(headerView[hlen:])
+ tcp.SetDestinationPort(rt.MinPort)
+ default:
+ return RuleDrop, ""
+ }
return RuleAccept, ""
}
diff --git a/pkg/tcpip/iptables/types.go b/pkg/tcpip/iptables/types.go
index 5735d001b..0102831d0 100644
--- a/pkg/tcpip/iptables/types.go
+++ b/pkg/tcpip/iptables/types.go
@@ -63,7 +63,7 @@ const (
// TableAccept indicates the packet should continue through netstack.
TableAccept TableVerdict = iota
- // TableAccept indicates the packet should be dropped.
+ // TableDrop indicates the packet should be dropped.
TableDrop
)
@@ -175,5 +175,5 @@ type Target interface {
// Action takes an action on the packet and returns a verdict on how
// traversal should (or should not) continue. If the return value is
// Jump, it also returns the name of the chain to jump to.
- Action(packet tcpip.PacketBuffer) (RuleVerdict, string)
+ Action(packet tcpip.PacketBuffer, filter IPHeaderFilter) (RuleVerdict, string)
}
diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go
index 2028f5201..a75dc0322 100644
--- a/pkg/tcpip/stack/nic.go
+++ b/pkg/tcpip/stack/nic.go
@@ -1087,19 +1087,8 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, local tcpip.Link
// TODO(gvisor.dev/issue/170): Not supporting iptables for IPv6 yet.
if protocol == header.IPv4ProtocolNumber {
- newPkt := pkt.Clone()
-
- headerView := newPkt.Data.First()
- h := header.IPv4(headerView)
- newPkt.NetworkHeader = headerView[:h.HeaderLength()]
-
- hlen := int(h.HeaderLength())
- tlen := int(h.TotalLength())
- newPkt.Data.TrimFront(hlen)
- newPkt.Data.CapLength(tlen - hlen)
-
ipt := n.stack.IPTables()
- if ok := ipt.Check(iptables.Prerouting, newPkt); !ok {
+ if ok := ipt.Check(iptables.Prerouting, pkt); !ok {
// iptables is telling us to drop the packet.
return
}
diff --git a/test/iptables/nat.go b/test/iptables/nat.go
index 306cbd1b3..899d1c9d3 100644
--- a/test/iptables/nat.go
+++ b/test/iptables/nat.go
@@ -71,20 +71,12 @@ func (NATRedirectTCPPort) ContainerAction(ip net.IP) error {
}
// Listen for TCP packets on redirect port.
- if err := listenTCP(redirectPort, sendloopDuration); err != nil {
- return fmt.Errorf("connection on port %d should be accepted, but got error %v", redirectPort, err)
- }
-
- return nil
+ return listenTCP(redirectPort, sendloopDuration)
}
// LocalAction implements TestCase.LocalAction.
func (NATRedirectTCPPort) LocalAction(ip net.IP) error {
- if err := connectTCP(ip, dropPort, acceptPort, sendloopDuration); err != nil {
- return fmt.Errorf("connection destined to port %d should be accepted, but got error %v", dropPort, err)
- }
-
- return nil
+ return connectTCP(ip, dropPort, acceptPort, sendloopDuration)
}
// NATDropUDP tests that packets are not received in ports other than redirect port.