summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/abi/linux/netfilter.go45
-rw-r--r--pkg/sentry/socket/netfilter/netfilter.go176
-rw-r--r--pkg/tcpip/iptables/BUILD1
-rw-r--r--pkg/tcpip/iptables/iptables.go3
-rw-r--r--pkg/tcpip/iptables/types.go17
-rw-r--r--pkg/tcpip/iptables/udp_matcher.go127
-rw-r--r--pkg/tcpip/network/ipv4/ipv4.go10
-rw-r--r--test/iptables/filter_input.go46
8 files changed, 396 insertions, 29 deletions
diff --git a/pkg/abi/linux/netfilter.go b/pkg/abi/linux/netfilter.go
index 33fcc6c95..effed7976 100644
--- a/pkg/abi/linux/netfilter.go
+++ b/pkg/abi/linux/netfilter.go
@@ -198,6 +198,11 @@ type XTEntryMatch struct {
// SizeOfXTEntryMatch is the size of an XTEntryMatch.
const SizeOfXTEntryMatch = 32
+type KernelXTEntryMatch struct {
+ XTEntryMatch
+ Data []byte
+}
+
// XTEntryTarget holds a target for a rule. For example, it can specify that
// packets matching the rule should DROP, ACCEPT, or use an extension target.
// iptables-extension(8) has a list of possible targets.
@@ -340,3 +345,43 @@ func goString(cstring []byte) string {
}
return string(cstring)
}
+
+// XTUDP holds data for matching UDP packets. It corresponds to struct xt_udp
+// in include/uapi/linux/netfilter/xt_tcpudp.h.
+type XTUDP struct {
+ // SourcePortStart specifies the inclusive start of the range of source
+ // ports to which the matcher applies.
+ SourcePortStart uint16
+
+ // SourcePortEnd specifies the inclusive end of the range of source ports
+ // to which the matcher applies.
+ SourcePortEnd uint16
+
+ // DestinationPortStart specifies the start of the destination port
+ // range to which the matcher applies.
+ DestinationPortStart uint16
+
+ // DestinationPortEnd specifies the start of the destination port
+ // range to which the matcher applies.
+ DestinationPortEnd uint16
+
+ // InverseFlags flips the meaning of certain fields. See the
+ // TX_UDP_INV_* flags.
+ InverseFlags uint8
+
+ _ uint8
+}
+
+// SizeOfXTUDP is the size of an XTUDP.
+const SizeOfXTUDP = 10
+
+// Flags in XTUDP.InverseFlags. Corresponding constants are in
+// include/uapi/linux/netfilter/xt_tcpudp.h.
+const (
+ // Invert the meaning of SourcePortStart/End.
+ XT_UDP_INV_SRCPT = 0x01
+ // Invert the meaning of DestinationPortStart/End.
+ XT_UDP_INV_DSTPT = 0x02
+ // Enable all flags.
+ XT_UDP_INV_MASK = 0x03
+)
diff --git a/pkg/sentry/socket/netfilter/netfilter.go b/pkg/sentry/socket/netfilter/netfilter.go
index c65c36081..3ca22932d 100644
--- a/pkg/sentry/socket/netfilter/netfilter.go
+++ b/pkg/sentry/socket/netfilter/netfilter.go
@@ -196,7 +196,9 @@ func convertNetstackToBinary(tablename string, table iptables.Table) (linux.Kern
}
func marshalMatcher(matcher iptables.Matcher) []byte {
- switch matcher.(type) {
+ switch m := matcher.(type) {
+ case *iptables.UDPMatcher:
+ return marshalUDPMatcher(m)
default:
// TODO(gvisor.dev/issue/170): We don't support any matchers
// yet, so any call to marshalMatcher will panic.
@@ -204,6 +206,39 @@ func marshalMatcher(matcher iptables.Matcher) []byte {
}
}
+func marshalUDPMatcher(matcher *iptables.UDPMatcher) []byte {
+ linuxMatcher := linux.KernelXTEntryMatch{
+ XTEntryMatch: linux.XTEntryMatch{
+ MatchSize: linux.SizeOfXTEntryMatch + linux.SizeOfXTUDP,
+ // Name: "udp",
+ },
+ Data: make([]byte, linux.SizeOfXTUDP+22),
+ }
+ // copy(linuxMatcher.Name[:], "udp")
+ copy(linuxMatcher.Name[:], "udp")
+
+ // TODO: Must be aligned.
+ xtudp := linux.XTUDP{
+ SourcePortStart: matcher.Data.SourcePortStart,
+ SourcePortEnd: matcher.Data.SourcePortEnd,
+ DestinationPortStart: matcher.Data.DestinationPortStart,
+ DestinationPortEnd: matcher.Data.DestinationPortEnd,
+ InverseFlags: matcher.Data.InverseFlags,
+ }
+ binary.Marshal(linuxMatcher.Data[:linux.SizeOfXTUDP], usermem.ByteOrder, xtudp)
+
+ if binary.Size(linuxMatcher)%64 != 0 {
+ panic(fmt.Sprintf("size is actually: %d", binary.Size(linuxMatcher)))
+ }
+
+ var buf [linux.SizeOfXTEntryMatch + linux.SizeOfXTUDP + 22]byte
+ if len(buf)%64 != 0 {
+ panic(fmt.Sprintf("len is actually: %d", len(buf)))
+ }
+ binary.Marshal(buf[:], usermem.ByteOrder, linuxMatcher)
+ return buf[:]
+}
+
func marshalTarget(target iptables.Target) []byte {
switch target.(type) {
case iptables.UnconditionalAcceptTarget:
@@ -218,6 +253,7 @@ func marshalTarget(target iptables.Target) []byte {
}
func marshalStandardTarget(verdict iptables.Verdict) []byte {
+ // TODO: Must be aligned.
// The target's name will be the empty string.
target := linux.XTStandardTarget{
Target: linux.XTEntryTarget{
@@ -318,10 +354,12 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
}
var entry linux.IPTEntry
buf := optVal[:linux.SizeOfIPTEntry]
- optVal = optVal[linux.SizeOfIPTEntry:]
binary.Unmarshal(buf, usermem.ByteOrder, &entry)
- if entry.TargetOffset != linux.SizeOfIPTEntry {
- // TODO(gvisor.dev/issue/170): Support matchers.
+ initialOptValLen := len(optVal)
+ optVal = optVal[linux.SizeOfIPTEntry:]
+
+ if entry.TargetOffset < linux.SizeOfIPTEntry {
+ log.Warningf("netfilter: entry has too-small target offset %d", entry.TargetOffset)
return syserr.ErrInvalidArgument
}
@@ -332,19 +370,41 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
return err
}
+ // TODO: Matchers (and maybe targets) can specify that they only work for certiain protocols, hooks, tables.
+ // Get matchers.
+ matchersSize := entry.TargetOffset - linux.SizeOfIPTEntry
+ if len(optVal) < int(matchersSize) {
+ log.Warningf("netfilter: entry doesn't have enough room for its matchers (only %d bytes remain)", len(optVal))
+ }
+ matchers, err := parseMatchers(filter, optVal[:matchersSize])
+ if err != nil {
+ log.Warningf("netfilter: failed to parse matchers: %v", err)
+ return err
+ }
+ optVal = optVal[matchersSize:]
+
// Get the target of the rule.
- target, consumed, err := parseTarget(optVal)
+ targetSize := entry.NextOffset - entry.TargetOffset
+ if len(optVal) < int(targetSize) {
+ log.Warningf("netfilter: entry doesn't have enough room for its target (only %d bytes remain)", len(optVal))
+ }
+ target, err := parseTarget(optVal[:targetSize])
if err != nil {
return err
}
- optVal = optVal[consumed:]
+ optVal = optVal[targetSize:]
table.Rules = append(table.Rules, iptables.Rule{
- Filter: filter,
- Target: target,
+ Filter: filter,
+ Target: target,
+ Matchers: matchers,
})
offsets = append(offsets, offset)
- offset += linux.SizeOfIPTEntry + consumed
+ offset += uint32(entry.NextOffset)
+
+ if initialOptValLen-len(optVal) != int(entry.NextOffset) {
+ log.Warningf("netfilter: entry NextOffset is %d, but entry took up %d bytes", entry.NextOffset, initialOptValLen-len(optVal))
+ }
}
// Go through the list of supported hooks for this table and, for each
@@ -401,12 +461,80 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
return nil
}
-// parseTarget parses a target from the start of optVal and returns the target
-// along with the number of bytes it occupies in optVal.
-func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
+// parseMatchers parses 0 or more matchers from optVal. optVal should contain
+// only the matchers.
+func parseMatchers(filter iptables.IPHeaderFilter, optVal []byte) ([]iptables.Matcher, *syserr.Error) {
+ var matchers []iptables.Matcher
+ for len(optVal) > 0 {
+ log.Infof("parseMatchers: optVal has len %d", len(optVal))
+ // Get the XTEntryMatch.
+ if len(optVal) < linux.SizeOfXTEntryMatch {
+ log.Warningf("netfilter: optVal has insufficient size for entry match: %d", len(optVal))
+ return nil, syserr.ErrInvalidArgument
+ }
+ var match linux.XTEntryMatch
+ buf := optVal[:linux.SizeOfXTEntryMatch]
+ binary.Unmarshal(buf, usermem.ByteOrder, &match)
+ log.Infof("parseMatchers: parsed entry match %q: %+v", match.Name.String(), match)
+
+ // Check some invariants.
+ if match.MatchSize < linux.SizeOfXTEntryMatch {
+ log.Warningf("netfilter: match size is too small, must be at least %d", linux.SizeOfXTEntryMatch)
+ return nil, syserr.ErrInvalidArgument
+ }
+ if len(optVal) < int(match.MatchSize) {
+ log.Warningf("netfilter: optVal has insufficient size for match: %d", len(optVal))
+ return nil, syserr.ErrInvalidArgument
+ }
+
+ buf = optVal[linux.SizeOfXTEntryMatch:match.MatchSize]
+ var matcher iptables.Matcher
+ var err error
+ switch match.Name.String() {
+ case "udp":
+ if len(buf) < linux.SizeOfXTUDP {
+ log.Warningf("netfilter: optVal has insufficient size for UDP match: %d", len(optVal))
+ return nil, syserr.ErrInvalidArgument
+ }
+ var matchData linux.XTUDP
+ // For alignment reasons, the match's total size may exceed what's
+ // strictly necessary to hold matchData.
+ binary.Unmarshal(buf[:linux.SizeOfXTUDP], usermem.ByteOrder, &matchData)
+ log.Infof("parseMatchers: parsed XTUDP: %+v", matchData)
+ matcher, err = iptables.NewUDPMatcher(filter, iptables.UDPMatcherData{
+ SourcePortStart: matchData.SourcePortStart,
+ SourcePortEnd: matchData.SourcePortEnd,
+ DestinationPortStart: matchData.DestinationPortStart,
+ DestinationPortEnd: matchData.DestinationPortEnd,
+ InverseFlags: matchData.InverseFlags,
+ })
+ if err != nil {
+ log.Warningf("netfilter: failed to create UDP matcher: %v", err)
+ return nil, syserr.ErrInvalidArgument
+ }
+
+ default:
+ log.Warningf("netfilter: unsupported matcher with name %q", match.Name.String())
+ return nil, syserr.ErrInvalidArgument
+ }
+
+ matchers = append(matchers, matcher)
+
+ // TODO: Support revision.
+ // TODO: Support proto -- matchers usually specify which proto(s) they work with.
+ optVal = optVal[match.MatchSize:]
+ }
+
+ // TODO: Check that optVal is exhausted.
+ return matchers, nil
+}
+
+// parseTarget parses a target from optVal. optVal should contain only the
+// target.
+func parseTarget(optVal []byte) (iptables.Target, *syserr.Error) {
if len(optVal) < linux.SizeOfXTEntryTarget {
log.Warningf("netfilter: optVal has insufficient size for entry target %d", len(optVal))
- return nil, 0, syserr.ErrInvalidArgument
+ return nil, syserr.ErrInvalidArgument
}
var target linux.XTEntryTarget
buf := optVal[:linux.SizeOfXTEntryTarget]
@@ -414,9 +542,9 @@ func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
switch target.Name.String() {
case "":
// Standard target.
- if len(optVal) < linux.SizeOfXTStandardTarget {
- log.Warningf("netfilter.SetEntries: optVal has insufficient size for standard target %d", len(optVal))
- return nil, 0, syserr.ErrInvalidArgument
+ if len(optVal) != linux.SizeOfXTStandardTarget {
+ log.Warningf("netfilter.SetEntries: optVal has wrong size for standard target %d", len(optVal))
+ return nil, syserr.ErrInvalidArgument
}
var standardTarget linux.XTStandardTarget
buf = optVal[:linux.SizeOfXTStandardTarget]
@@ -424,22 +552,22 @@ func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
verdict, err := translateToStandardVerdict(standardTarget.Verdict)
if err != nil {
- return nil, 0, err
+ return nil, err
}
switch verdict {
case iptables.Accept:
- return iptables.UnconditionalAcceptTarget{}, linux.SizeOfXTStandardTarget, nil
+ return iptables.UnconditionalAcceptTarget{}, nil
case iptables.Drop:
- return iptables.UnconditionalDropTarget{}, linux.SizeOfXTStandardTarget, nil
+ return iptables.UnconditionalDropTarget{}, nil
default:
panic(fmt.Sprintf("Unknown verdict: %v", verdict))
}
case errorTargetName:
// Error target.
- if len(optVal) < linux.SizeOfXTErrorTarget {
+ if len(optVal) != linux.SizeOfXTErrorTarget {
log.Infof("netfilter.SetEntries: optVal has insufficient size for error target %d", len(optVal))
- return nil, 0, syserr.ErrInvalidArgument
+ return nil, syserr.ErrInvalidArgument
}
var errorTarget linux.XTErrorTarget
buf = optVal[:linux.SizeOfXTErrorTarget]
@@ -454,16 +582,16 @@ func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
// rules have an error with the name of the chain.
switch errorTarget.Name.String() {
case errorTargetName:
- return iptables.ErrorTarget{}, linux.SizeOfXTErrorTarget, nil
+ return iptables.ErrorTarget{}, nil
default:
log.Infof("Unknown error target %q doesn't exist or isn't supported yet.", errorTarget.Name.String())
- return nil, 0, syserr.ErrInvalidArgument
+ return nil, syserr.ErrInvalidArgument
}
}
// Unknown target.
log.Infof("Unknown target %q doesn't exist or isn't supported yet.", target.Name.String())
- return nil, 0, syserr.ErrInvalidArgument
+ return nil, syserr.ErrInvalidArgument
}
func filterFromIPTIP(iptip linux.IPTIP) (iptables.IPHeaderFilter, *syserr.Error) {
diff --git a/pkg/tcpip/iptables/BUILD b/pkg/tcpip/iptables/BUILD
index 297eaccaf..e41c645ed 100644
--- a/pkg/tcpip/iptables/BUILD
+++ b/pkg/tcpip/iptables/BUILD
@@ -8,6 +8,7 @@ go_library(
"iptables.go",
"targets.go",
"types.go",
+ "udp_matcher.go",
],
importpath = "gvisor.dev/gvisor/pkg/tcpip/iptables",
visibility = ["//visibility:public"],
diff --git a/pkg/tcpip/iptables/iptables.go b/pkg/tcpip/iptables/iptables.go
index fc06b5b87..accedba1e 100644
--- a/pkg/tcpip/iptables/iptables.go
+++ b/pkg/tcpip/iptables/iptables.go
@@ -138,6 +138,8 @@ func EmptyFilterTable() Table {
// Check runs pkt through the rules for hook. It returns true when the packet
// should continue traversing the network stack and false when it should be
// dropped.
+//
+// Precondition: pkt.NetworkHeader is set.
func (it *IPTables) Check(hook Hook, pkt tcpip.PacketBuffer) bool {
// TODO(gvisor.dev/issue/170): A lot of this is uncomplicated because
// we're missing features. Jumps, the call stack, etc. aren't checked
@@ -163,6 +165,7 @@ func (it *IPTables) Check(hook Hook, pkt tcpip.PacketBuffer) bool {
return true
}
+// Precondition: pkt.NetworkHeader is set.
func (it *IPTables) checkTable(hook Hook, pkt tcpip.PacketBuffer, tablename string) Verdict {
// Start from ruleIdx and walk the list of rules until a rule gives us
// a verdict.
diff --git a/pkg/tcpip/iptables/types.go b/pkg/tcpip/iptables/types.go
index a8b972f1b..d47447d40 100644
--- a/pkg/tcpip/iptables/types.go
+++ b/pkg/tcpip/iptables/types.go
@@ -169,12 +169,29 @@ type IPHeaderFilter struct {
Protocol tcpip.TransportProtocolNumber
}
+// TODO: Should these be able to marshal/unmarshal themselves?
+// TODO: Something has to map the name to the matcher.
// A Matcher is the interface for matching packets.
type Matcher interface {
// Match returns whether the packet matches and whether the packet
// should be "hotdropped", i.e. dropped immediately. This is usually
// used for suspicious packets.
+ //
+ // Precondition: packet.NetworkHeader is set.
Match(hook Hook, packet tcpip.PacketBuffer, interfaceName string) (matches bool, hotdrop bool)
+
+ // TODO: Make this typesafe by having each Matcher have their own, typed CheckEntry?
+ // CheckEntry(params MatchCheckEntryParams) bool
+}
+
+// TODO: Unused?
+type MatchCheckEntryParams struct {
+ Table string // TODO: Tables should be an enum...
+ Filter IPHeaderFilter
+ Info interface{} // TODO: Type unsafe.
+ // HookMask uint8
+ // Family uint8
+ // NFTCompat bool
}
// A Target is the interface for taking an action for a packet.
diff --git a/pkg/tcpip/iptables/udp_matcher.go b/pkg/tcpip/iptables/udp_matcher.go
new file mode 100644
index 000000000..65ae7f9e0
--- /dev/null
+++ b/pkg/tcpip/iptables/udp_matcher.go
@@ -0,0 +1,127 @@
+// 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 iptables
+
+import (
+ "fmt"
+ "runtime/debug"
+
+ "gvisor.dev/gvisor/pkg/log"
+ "gvisor.dev/gvisor/pkg/tcpip"
+ "gvisor.dev/gvisor/pkg/tcpip/header"
+)
+
+type UDPMatcher struct {
+ Data UDPMatcherData
+
+ // tablename string
+ // unsigned int matchsize;
+ // unsigned int usersize;
+ // #ifdef CONFIG_COMPAT
+ // unsigned int compatsize;
+ // #endif
+ // unsigned int hooks;
+ // unsigned short proto;
+ // unsigned short family;
+}
+
+// TODO: Delete?
+// MatchCheckEntryParams
+
+type UDPMatcherData struct {
+ // Filter IPHeaderFilter
+
+ SourcePortStart uint16
+ SourcePortEnd uint16
+ DestinationPortStart uint16
+ DestinationPortEnd uint16
+ InverseFlags uint8
+}
+
+func NewUDPMatcher(filter IPHeaderFilter, data UDPMatcherData) (Matcher, error) {
+ // TODO: We currently only support source port and destination port.
+ log.Infof("Adding rule with UDPMatcherData: %+v", data)
+
+ if data.InverseFlags != 0 {
+ return nil, fmt.Errorf("unsupported UDP matcher flags set")
+ }
+
+ if filter.Protocol != header.UDPProtocolNumber {
+ return nil, fmt.Errorf("UDP matching is only valid for protocol %d.", header.UDPProtocolNumber)
+ }
+
+ return &UDPMatcher{Data: data}, nil
+}
+
+// TODO: Check xt_tcpudp.c. Need to check for same things (e.g. fragments).
+func (um *UDPMatcher) Match(hook Hook, pkt tcpip.PacketBuffer, interfaceName string) (bool, bool) {
+ log.Infof("UDPMatcher called from: %s", string(debug.Stack()))
+ netHeader := header.IPv4(pkt.NetworkHeader)
+
+ // TODO: Do we check proto here or elsewhere? I think elsewhere (check
+ // codesearch).
+ if netHeader.TransportProtocol() != header.UDPProtocolNumber {
+ log.Infof("UDPMatcher: wrong protocol number")
+ return false, false
+ }
+
+ // We dont't match fragments.
+ if frag := netHeader.FragmentOffset(); frag != 0 {
+ log.Infof("UDPMatcher: it's a fragment")
+ if frag == 1 {
+ return false, true
+ }
+ log.Warningf("Dropping UDP packet: malicious fragmented packet.")
+ return false, false
+ }
+
+ // Now we need the transport header. However, this may not have been set
+ // yet.
+ // TODO
+ var udpHeader header.UDP
+ if pkt.TransportHeader != nil {
+ log.Infof("UDPMatcher: transport header is not nil")
+ udpHeader = header.UDP(pkt.TransportHeader)
+ } else {
+ log.Infof("UDPMatcher: transport header is nil")
+ log.Infof("UDPMatcher: is network header nil: %t", pkt.NetworkHeader == nil)
+ // The UDP header hasn't been parsed yet. We have to do it here.
+ if len(pkt.Data.First()) < header.UDPMinimumSize {
+ // There's no valid UDP header here, so we hotdrop the
+ // packet.
+ // TODO: Stats.
+ log.Warningf("Dropping UDP packet: size to small.")
+ return false, true
+ }
+ udpHeader = header.UDP(pkt.Data.First())
+ }
+
+ // Check whether the source and destination ports are within the
+ // matching range.
+ sourcePort := udpHeader.SourcePort()
+ destinationPort := udpHeader.DestinationPort()
+ log.Infof("UDPMatcher: sport and dport are %d and %d. sports and dport start and end are (%d, %d) and (%d, %d)",
+ udpHeader.SourcePort(), udpHeader.DestinationPort(),
+ um.Data.SourcePortStart, um.Data.SourcePortEnd,
+ um.Data.DestinationPortStart, um.Data.DestinationPortEnd)
+ if sourcePort < um.Data.SourcePortStart || um.Data.SourcePortEnd < sourcePort {
+ return false, false
+ }
+ if destinationPort < um.Data.DestinationPortStart || um.Data.DestinationPortEnd < destinationPort {
+ return false, false
+ }
+
+ return true, false
+}
diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go
index 85512f9b2..6597e6781 100644
--- a/pkg/tcpip/network/ipv4/ipv4.go
+++ b/pkg/tcpip/network/ipv4/ipv4.go
@@ -353,6 +353,11 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt tcpip.PacketBuffer) {
}
pkt.NetworkHeader = headerView[:h.HeaderLength()]
+ hlen := int(h.HeaderLength())
+ tlen := int(h.TotalLength())
+ pkt.Data.TrimFront(hlen)
+ pkt.Data.CapLength(tlen - hlen)
+
// iptables filtering. All packets that reach here are intended for
// this machine and will not be forwarded.
ipt := e.stack.IPTables()
@@ -361,11 +366,6 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt tcpip.PacketBuffer) {
return
}
- hlen := int(h.HeaderLength())
- tlen := int(h.TotalLength())
- pkt.Data.TrimFront(hlen)
- pkt.Data.CapLength(tlen - hlen)
-
more := (h.Flags() & header.IPv4FlagMoreFragments) != 0
if more || h.FragmentOffset() != 0 {
if pkt.Data.Size() == 0 {
diff --git a/test/iptables/filter_input.go b/test/iptables/filter_input.go
index fd02ff2ff..bc963d40e 100644
--- a/test/iptables/filter_input.go
+++ b/test/iptables/filter_input.go
@@ -15,6 +15,7 @@
package iptables
import (
+ "errors"
"fmt"
"net"
"time"
@@ -248,3 +249,48 @@ func (FilterInputDropAll) ContainerAction(ip net.IP) error {
func (FilterInputDropAll) LocalAction(ip net.IP) error {
return sendUDPLoop(ip, dropPort, sendloopDuration)
}
+
+// FilterInputMultiUDPRules verifies that multiple UDP rules are applied
+// correctly. This has the added benefit of testing whether we're serializing
+// rules correctly -- if we do it incorrectly, the iptables tool will
+// misunderstand and save the wrong tables.
+type FilterInputMultiUDPRules struct{}
+
+func (FilterInputMultiUDPRules) Name() string {
+ return "FilterInputMultiUDPRules"
+}
+
+func (FilterInputMultiUDPRules) ContainerAction(ip net.IP) error {
+ if err := filterTable("-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
+ return err
+ }
+ // if err := filterTable("-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", acceptPort), "-j", "ACCEPT"); err != nil {
+ // return err
+ // }
+ return filterTable("-L")
+}
+
+func (FilterInputMultiUDPRules) LocalAction(ip net.IP) error {
+ // No-op.
+ return nil
+}
+
+// FilterInputRequireProtocolUDP checks that "-m udp" requires "-p udp" to be
+// specified.
+type FilterInputRequireProtocolUDP struct{}
+
+func (FilterInputRequireProtocolUDP) Name() string {
+ return "FilterInputRequireProtocolUDP"
+}
+
+func (FilterInputRequireProtocolUDP) ContainerAction(ip net.IP) error {
+ if err := filterTable("-A", "INPUT", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err == nil {
+ return errors.New("expected iptables to fail with out \"-p udp\", but succeeded")
+ }
+ return nil
+}
+
+func (FilterInputRequireProtocolUDP) LocalAction(ip net.IP) error {
+ // No-op.
+ return nil
+}