Merge pull request #1943 from kevinGC:ipt-filter-ip
PiperOrigin-RevId: 301197007
This commit is contained in:
commit
159a230b9b
|
@ -158,10 +158,32 @@ type IPTIP struct {
|
|||
// Flags define matching behavior for the IP header.
|
||||
Flags uint8
|
||||
|
||||
// InverseFlags invert the meaning of fields in struct IPTIP.
|
||||
// InverseFlags invert the meaning of fields in struct IPTIP. See the
|
||||
// IPT_INV_* flags.
|
||||
InverseFlags uint8
|
||||
}
|
||||
|
||||
// Flags in IPTIP.InverseFlags. Corresponding constants are in
|
||||
// include/uapi/linux/netfilter_ipv4/ip_tables.h.
|
||||
const (
|
||||
// Invert the meaning of InputInterface.
|
||||
IPT_INV_VIA_IN = 0x01
|
||||
// Invert the meaning of OutputInterface.
|
||||
IPT_INV_VIA_OUT = 0x02
|
||||
// Unclear what this is, as no references to it exist in the kernel.
|
||||
IPT_INV_TOS = 0x04
|
||||
// Invert the meaning of Src.
|
||||
IPT_INV_SRCIP = 0x08
|
||||
// Invert the meaning of Dst.
|
||||
IPT_INV_DSTIP = 0x10
|
||||
// Invert the meaning of the IPT_F_FRAG flag.
|
||||
IPT_INV_FRAG = 0x20
|
||||
// Invert the meaning of the Protocol field.
|
||||
IPT_INV_PROTO = 0x40
|
||||
// Enable all flags.
|
||||
IPT_INV_MASK = 0x7F
|
||||
)
|
||||
|
||||
// SizeOfIPTIP is the size of an IPTIP.
|
||||
const SizeOfIPTIP = 84
|
||||
|
||||
|
|
|
@ -703,25 +703,34 @@ func filterFromIPTIP(iptip linux.IPTIP) (iptables.IPHeaderFilter, error) {
|
|||
if containsUnsupportedFields(iptip) {
|
||||
return iptables.IPHeaderFilter{}, fmt.Errorf("unsupported fields in struct iptip: %+v", iptip)
|
||||
}
|
||||
if len(iptip.Dst) != header.IPv4AddressSize || len(iptip.DstMask) != header.IPv4AddressSize {
|
||||
return iptables.IPHeaderFilter{}, fmt.Errorf("incorrect length of destination (%d) and/or destination mask (%d) fields", len(iptip.Dst), len(iptip.DstMask))
|
||||
}
|
||||
return iptables.IPHeaderFilter{
|
||||
Protocol: tcpip.TransportProtocolNumber(iptip.Protocol),
|
||||
Protocol: tcpip.TransportProtocolNumber(iptip.Protocol),
|
||||
Dst: tcpip.Address(iptip.Dst[:]),
|
||||
DstMask: tcpip.Address(iptip.DstMask[:]),
|
||||
DstInvert: iptip.InverseFlags&linux.IPT_INV_DSTIP != 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func containsUnsupportedFields(iptip linux.IPTIP) bool {
|
||||
// Currently we check that everything except protocol is zeroed.
|
||||
// The following features are supported:
|
||||
// - Protocol
|
||||
// - Dst and DstMask
|
||||
// - The inverse destination IP check flag
|
||||
var emptyInetAddr = linux.InetAddr{}
|
||||
var emptyInterface = [linux.IFNAMSIZ]byte{}
|
||||
return iptip.Dst != emptyInetAddr ||
|
||||
iptip.Src != emptyInetAddr ||
|
||||
// Disable any supported inverse flags.
|
||||
inverseMask := uint8(linux.IPT_INV_DSTIP)
|
||||
return iptip.Src != emptyInetAddr ||
|
||||
iptip.SrcMask != emptyInetAddr ||
|
||||
iptip.DstMask != emptyInetAddr ||
|
||||
iptip.InputInterface != emptyInterface ||
|
||||
iptip.OutputInterface != emptyInterface ||
|
||||
iptip.InputInterfaceMask != emptyInterface ||
|
||||
iptip.OutputInterfaceMask != emptyInterface ||
|
||||
iptip.Flags != 0 ||
|
||||
iptip.InverseFlags != 0
|
||||
iptip.InverseFlags&^inverseMask != 0
|
||||
}
|
||||
|
||||
func validUnderflow(rule iptables.Rule) bool {
|
||||
|
|
|
@ -267,9 +267,8 @@ func (it *IPTables) checkRule(hook Hook, pkt tcpip.PacketBuffer, table Table, ru
|
|||
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() {
|
||||
// Check whether the packet matches the IP header filter.
|
||||
if !filterMatch(rule.Filter, header.IPv4(pkt.NetworkHeader)) {
|
||||
// Continue on to the next rule.
|
||||
return RuleJump, ruleIdx + 1
|
||||
}
|
||||
|
@ -290,3 +289,26 @@ func (it *IPTables) checkRule(hook Hook, pkt tcpip.PacketBuffer, table Table, ru
|
|||
// All the matchers matched, so run the target.
|
||||
return rule.Target.Action(pkt)
|
||||
}
|
||||
|
||||
func filterMatch(filter IPHeaderFilter, hdr header.IPv4) bool {
|
||||
// TODO(gvisor.dev/issue/170): Support other fields of the filter.
|
||||
// Check the transport protocol.
|
||||
if filter.Protocol != 0 && filter.Protocol != hdr.TransportProtocol() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check the destination IP.
|
||||
dest := hdr.DestinationAddress()
|
||||
matches := true
|
||||
for i := range filter.Dst {
|
||||
if dest[i]&filter.DstMask[i] != filter.Dst[i] {
|
||||
matches = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if matches == filter.DstInvert {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -144,6 +144,18 @@ type Rule struct {
|
|||
type IPHeaderFilter struct {
|
||||
// Protocol matches the transport protocol.
|
||||
Protocol tcpip.TransportProtocolNumber
|
||||
|
||||
// Dst matches the destination IP address.
|
||||
Dst tcpip.Address
|
||||
|
||||
// DstMask masks bits of the destination IP address when comparing with
|
||||
// Dst.
|
||||
DstMask tcpip.Address
|
||||
|
||||
// DstInvert inverts the meaning of the destination IP check, i.e. when
|
||||
// true the filter will match packets that fail the destination
|
||||
// comparison.
|
||||
DstInvert bool
|
||||
}
|
||||
|
||||
// A Matcher is the interface for matching packets.
|
||||
|
|
|
@ -47,6 +47,8 @@ func init() {
|
|||
RegisterTestCase(FilterInputJumpReturnDrop{})
|
||||
RegisterTestCase(FilterInputJumpBuiltin{})
|
||||
RegisterTestCase(FilterInputJumpTwice{})
|
||||
RegisterTestCase(FilterInputDestination{})
|
||||
RegisterTestCase(FilterInputInvertDestination{})
|
||||
}
|
||||
|
||||
// FilterInputDropUDP tests that we can drop UDP traffic.
|
||||
|
@ -596,3 +598,66 @@ func (FilterInputJumpTwice) ContainerAction(ip net.IP) error {
|
|||
func (FilterInputJumpTwice) LocalAction(ip net.IP) error {
|
||||
return sendUDPLoop(ip, acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// FilterInputDestination verifies that we can filter packets via `-d
|
||||
// <ipaddr>`.
|
||||
type FilterInputDestination struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
func (FilterInputDestination) Name() string {
|
||||
return "FilterInputDestination"
|
||||
}
|
||||
|
||||
// ContainerAction implements TestCase.ContainerAction.
|
||||
func (FilterInputDestination) ContainerAction(ip net.IP) error {
|
||||
addrs, err := localAddrs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make INPUT's default action DROP, then ACCEPT all packets bound for
|
||||
// this machine.
|
||||
rules := [][]string{{"-P", "INPUT", "DROP"}}
|
||||
for _, addr := range addrs {
|
||||
rules = append(rules, []string{"-A", "INPUT", "-d", addr, "-j", "ACCEPT"})
|
||||
}
|
||||
if err := filterTableRules(rules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return listenUDP(acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// LocalAction implements TestCase.LocalAction.
|
||||
func (FilterInputDestination) LocalAction(ip net.IP) error {
|
||||
return sendUDPLoop(ip, acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// FilterInputInvertDestination verifies that we can filter packets via `! -d
|
||||
// <ipaddr>`.
|
||||
type FilterInputInvertDestination struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
func (FilterInputInvertDestination) Name() string {
|
||||
return "FilterInputInvertDestination"
|
||||
}
|
||||
|
||||
// ContainerAction implements TestCase.ContainerAction.
|
||||
func (FilterInputInvertDestination) ContainerAction(ip net.IP) error {
|
||||
// Make INPUT's default action DROP, then ACCEPT all packets not bound
|
||||
// for 127.0.0.1.
|
||||
rules := [][]string{
|
||||
{"-P", "INPUT", "DROP"},
|
||||
{"-A", "INPUT", "!", "-d", localIP, "-j", "ACCEPT"},
|
||||
}
|
||||
if err := filterTableRules(rules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return listenUDP(acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// LocalAction implements TestCase.LocalAction.
|
||||
func (FilterInputInvertDestination) LocalAction(ip net.IP) error {
|
||||
return sendUDPLoop(ip, acceptPort, sendloopDuration)
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import (
|
|||
func init() {
|
||||
RegisterTestCase(FilterOutputDropTCPDestPort{})
|
||||
RegisterTestCase(FilterOutputDropTCPSrcPort{})
|
||||
RegisterTestCase(FilterOutputDestination{})
|
||||
RegisterTestCase(FilterOutputInvertDestination{})
|
||||
}
|
||||
|
||||
// FilterOutputDropTCPDestPort tests that connections are not accepted on
|
||||
|
@ -87,3 +89,57 @@ func (FilterOutputDropTCPSrcPort) LocalAction(ip net.IP) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterOutputDestination tests that we can selectively allow packets to
|
||||
// certain destinations.
|
||||
type FilterOutputDestination struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
func (FilterOutputDestination) Name() string {
|
||||
return "FilterOutputDestination"
|
||||
}
|
||||
|
||||
// ContainerAction implements TestCase.ContainerAction.
|
||||
func (FilterOutputDestination) ContainerAction(ip net.IP) error {
|
||||
rules := [][]string{
|
||||
{"-A", "OUTPUT", "-d", ip.String(), "-j", "ACCEPT"},
|
||||
{"-P", "OUTPUT", "DROP"},
|
||||
}
|
||||
if err := filterTableRules(rules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sendUDPLoop(ip, acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// LocalAction implements TestCase.LocalAction.
|
||||
func (FilterOutputDestination) LocalAction(ip net.IP) error {
|
||||
return listenUDP(acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// FilterOutputInvertDestination tests that we can selectively allow packets
|
||||
// not headed for a particular destination.
|
||||
type FilterOutputInvertDestination struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
func (FilterOutputInvertDestination) Name() string {
|
||||
return "FilterOutputInvertDestination"
|
||||
}
|
||||
|
||||
// ContainerAction implements TestCase.ContainerAction.
|
||||
func (FilterOutputInvertDestination) ContainerAction(ip net.IP) error {
|
||||
rules := [][]string{
|
||||
{"-A", "OUTPUT", "!", "-d", localIP, "-j", "ACCEPT"},
|
||||
{"-P", "OUTPUT", "DROP"},
|
||||
}
|
||||
if err := filterTableRules(rules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sendUDPLoop(ip, acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// LocalAction implements TestCase.LocalAction.
|
||||
func (FilterOutputInvertDestination) LocalAction(ip net.IP) error {
|
||||
return listenUDP(acceptPort, sendloopDuration)
|
||||
}
|
||||
|
|
|
@ -303,3 +303,69 @@ func TestJumpTwice(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInputDestination(t *testing.T) {
|
||||
if err := singleTest(FilterInputDestination{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInputInvertDestination(t *testing.T) {
|
||||
if err := singleTest(FilterInputInvertDestination{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputDestination(t *testing.T) {
|
||||
if err := singleTest(FilterOutputDestination{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputInvertDestination(t *testing.T) {
|
||||
if err := singleTest(FilterOutputInvertDestination{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNATOutRedirectIP(t *testing.T) {
|
||||
if err := singleTest(NATOutRedirectIP{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNATOutDontRedirectIP(t *testing.T) {
|
||||
if err := singleTest(NATOutDontRedirectIP{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNATOutRedirectInvert(t *testing.T) {
|
||||
if err := singleTest(NATOutRedirectInvert{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNATPreRedirectIP(t *testing.T) {
|
||||
if err := singleTest(NATPreRedirectIP{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNATPreDontRedirectIP(t *testing.T) {
|
||||
if err := singleTest(NATPreDontRedirectIP{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNATPreRedirectInvert(t *testing.T) {
|
||||
if err := singleTest(NATPreRedirectInvert{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNATRedirectRequiresProtocol(t *testing.T) {
|
||||
if err := singleTest(NATRedirectRequiresProtocol{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
)
|
||||
|
||||
const iptablesBinary = "iptables"
|
||||
const localIP = "127.0.0.1"
|
||||
|
||||
// filterTable calls `iptables -t filter` with the given args.
|
||||
func filterTable(args ...string) error {
|
||||
|
@ -46,8 +47,17 @@ func tableCmd(table string, args []string) error {
|
|||
|
||||
// filterTableRules is like filterTable, but runs multiple iptables commands.
|
||||
func filterTableRules(argsList [][]string) error {
|
||||
return tableRules("filter", argsList)
|
||||
}
|
||||
|
||||
// natTableRules is like natTable, but runs multiple iptables commands.
|
||||
func natTableRules(argsList [][]string) error {
|
||||
return tableRules("nat", argsList)
|
||||
}
|
||||
|
||||
func tableRules(table string, argsList [][]string) error {
|
||||
for _, args := range argsList {
|
||||
if err := filterTable(args...); err != nil {
|
||||
if err := tableCmd(table, args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -146,3 +156,16 @@ func connectTCP(ip net.IP, port int, timeout time.Duration) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// localAddrs returns a list of local network interface addresses.
|
||||
func localAddrs() ([]string, error) {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrStrs := make([]string, 0, len(addrs))
|
||||
for _, addr := range addrs {
|
||||
addrStrs = append(addrStrs, addr.String())
|
||||
}
|
||||
return addrStrs, nil
|
||||
}
|
||||
|
|
|
@ -15,8 +15,10 @@
|
|||
package iptables
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -28,6 +30,13 @@ func init() {
|
|||
RegisterTestCase(NATRedirectTCPPort{})
|
||||
RegisterTestCase(NATDropUDP{})
|
||||
RegisterTestCase(NATAcceptAll{})
|
||||
RegisterTestCase(NATPreRedirectIP{})
|
||||
RegisterTestCase(NATPreDontRedirectIP{})
|
||||
RegisterTestCase(NATPreRedirectInvert{})
|
||||
RegisterTestCase(NATOutRedirectIP{})
|
||||
RegisterTestCase(NATOutDontRedirectIP{})
|
||||
RegisterTestCase(NATOutRedirectInvert{})
|
||||
RegisterTestCase(NATRedirectRequiresProtocol{})
|
||||
}
|
||||
|
||||
// NATRedirectUDPPort tests that packets are redirected to different port.
|
||||
|
@ -79,7 +88,8 @@ func (NATRedirectTCPPort) LocalAction(ip net.IP) error {
|
|||
return connectTCP(ip, dropPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// NATDropUDP tests that packets are not received in ports other than redirect port.
|
||||
// NATDropUDP tests that packets are not received in ports other than redirect
|
||||
// port.
|
||||
type NATDropUDP struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
|
@ -130,3 +140,192 @@ func (NATAcceptAll) ContainerAction(ip net.IP) error {
|
|||
func (NATAcceptAll) LocalAction(ip net.IP) error {
|
||||
return sendUDPLoop(ip, acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// NATOutRedirectIP uses iptables to select packets based on destination IP and
|
||||
// redirects them.
|
||||
type NATOutRedirectIP struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
func (NATOutRedirectIP) Name() string {
|
||||
return "NATOutRedirectIP"
|
||||
}
|
||||
|
||||
// ContainerAction implements TestCase.ContainerAction.
|
||||
func (NATOutRedirectIP) ContainerAction(ip net.IP) error {
|
||||
// Redirect OUTPUT packets to a listening localhost port.
|
||||
dest := net.IP([]byte{200, 0, 0, 2})
|
||||
return loopbackTest(dest, "-A", "OUTPUT", "-d", dest.String(), "-p", "udp", "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort))
|
||||
}
|
||||
|
||||
// LocalAction implements TestCase.LocalAction.
|
||||
func (NATOutRedirectIP) LocalAction(ip net.IP) error {
|
||||
// No-op.
|
||||
return nil
|
||||
}
|
||||
|
||||
// NATOutDontRedirectIP tests that iptables matching with "-d" does not match
|
||||
// packets it shouldn't.
|
||||
type NATOutDontRedirectIP struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
func (NATOutDontRedirectIP) Name() string {
|
||||
return "NATOutDontRedirectIP"
|
||||
}
|
||||
|
||||
// ContainerAction implements TestCase.ContainerAction.
|
||||
func (NATOutDontRedirectIP) ContainerAction(ip net.IP) error {
|
||||
if err := natTable("-A", "OUTPUT", "-d", localIP, "-p", "udp", "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", dropPort)); err != nil {
|
||||
return err
|
||||
}
|
||||
return sendUDPLoop(ip, acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// LocalAction implements TestCase.LocalAction.
|
||||
func (NATOutDontRedirectIP) LocalAction(ip net.IP) error {
|
||||
return listenUDP(acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// NATOutRedirectInvert tests that iptables can match with "! -d".
|
||||
type NATOutRedirectInvert struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
func (NATOutRedirectInvert) Name() string {
|
||||
return "NATOutRedirectInvert"
|
||||
}
|
||||
|
||||
// ContainerAction implements TestCase.ContainerAction.
|
||||
func (NATOutRedirectInvert) ContainerAction(ip net.IP) error {
|
||||
// Redirect OUTPUT packets to a listening localhost port.
|
||||
dest := []byte{200, 0, 0, 3}
|
||||
destStr := "200.0.0.2"
|
||||
return loopbackTest(dest, "-A", "OUTPUT", "!", "-d", destStr, "-p", "udp", "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort))
|
||||
}
|
||||
|
||||
// LocalAction implements TestCase.LocalAction.
|
||||
func (NATOutRedirectInvert) LocalAction(ip net.IP) error {
|
||||
// No-op.
|
||||
return nil
|
||||
}
|
||||
|
||||
// NATPreRedirectIP tests that we can use iptables to select packets based on
|
||||
// destination IP and redirect them.
|
||||
type NATPreRedirectIP struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
func (NATPreRedirectIP) Name() string {
|
||||
return "NATPreRedirectIP"
|
||||
}
|
||||
|
||||
// ContainerAction implements TestCase.ContainerAction.
|
||||
func (NATPreRedirectIP) ContainerAction(ip net.IP) error {
|
||||
addrs, err := localAddrs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rules [][]string
|
||||
for _, addr := range addrs {
|
||||
rules = append(rules, []string{"-A", "PREROUTING", "-p", "udp", "-d", addr, "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)})
|
||||
}
|
||||
if err := natTableRules(rules); err != nil {
|
||||
return err
|
||||
}
|
||||
return listenUDP(acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// LocalAction implements TestCase.LocalAction.
|
||||
func (NATPreRedirectIP) LocalAction(ip net.IP) error {
|
||||
return sendUDPLoop(ip, dropPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// NATPreDontRedirectIP tests that iptables matching with "-d" does not match
|
||||
// packets it shouldn't.
|
||||
type NATPreDontRedirectIP struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
func (NATPreDontRedirectIP) Name() string {
|
||||
return "NATPreDontRedirectIP"
|
||||
}
|
||||
|
||||
// ContainerAction implements TestCase.ContainerAction.
|
||||
func (NATPreDontRedirectIP) ContainerAction(ip net.IP) error {
|
||||
if err := natTable("-A", "PREROUTING", "-p", "udp", "-d", localIP, "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", dropPort)); err != nil {
|
||||
return err
|
||||
}
|
||||
return listenUDP(acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// LocalAction implements TestCase.LocalAction.
|
||||
func (NATPreDontRedirectIP) LocalAction(ip net.IP) error {
|
||||
return sendUDPLoop(ip, acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// NATPreRedirectInvert tests that iptables can match with "! -d".
|
||||
type NATPreRedirectInvert struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
func (NATPreRedirectInvert) Name() string {
|
||||
return "NATPreRedirectInvert"
|
||||
}
|
||||
|
||||
// ContainerAction implements TestCase.ContainerAction.
|
||||
func (NATPreRedirectInvert) ContainerAction(ip net.IP) error {
|
||||
if err := natTable("-A", "PREROUTING", "-p", "udp", "!", "-d", localIP, "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err != nil {
|
||||
return err
|
||||
}
|
||||
return listenUDP(acceptPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// LocalAction implements TestCase.LocalAction.
|
||||
func (NATPreRedirectInvert) LocalAction(ip net.IP) error {
|
||||
return sendUDPLoop(ip, dropPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// NATRedirectRequiresProtocol tests that use of the --to-ports flag requires a
|
||||
// protocol to be specified with -p.
|
||||
type NATRedirectRequiresProtocol struct{}
|
||||
|
||||
// Name implements TestCase.Name.
|
||||
func (NATRedirectRequiresProtocol) Name() string {
|
||||
return "NATRedirectRequiresProtocol"
|
||||
}
|
||||
|
||||
// ContainerAction implements TestCase.ContainerAction.
|
||||
func (NATRedirectRequiresProtocol) ContainerAction(ip net.IP) error {
|
||||
if err := natTable("-A", "PREROUTING", "-d", localIP, "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err == nil {
|
||||
return errors.New("expected an error using REDIRECT --to-ports without a protocol")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LocalAction implements TestCase.LocalAction.
|
||||
func (NATRedirectRequiresProtocol) LocalAction(ip net.IP) error {
|
||||
// No-op.
|
||||
return nil
|
||||
}
|
||||
|
||||
// loopbackTests runs an iptables rule and ensures that packets sent to
|
||||
// dest:dropPort are received by localhost:acceptPort.
|
||||
func loopbackTest(dest net.IP, args ...string) error {
|
||||
if err := natTable(args...); err != nil {
|
||||
return err
|
||||
}
|
||||
sendCh := make(chan error)
|
||||
listenCh := make(chan error)
|
||||
go func() {
|
||||
sendCh <- sendUDPLoop(dest, dropPort, sendloopDuration)
|
||||
}()
|
||||
go func() {
|
||||
listenCh <- listenUDP(acceptPort, sendloopDuration)
|
||||
}()
|
||||
select {
|
||||
case err := <-listenCh:
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case <-time.After(sendloopDuration):
|
||||
return errors.New("timed out")
|
||||
}
|
||||
// sendCh will always take the full sendloop time.
|
||||
return <-sendCh
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue