summaryrefslogtreecommitdiffhomepage
path: root/test/iptables
diff options
context:
space:
mode:
Diffstat (limited to 'test/iptables')
-rw-r--r--test/iptables/BUILD2
-rw-r--r--test/iptables/filter_input.go217
-rw-r--r--test/iptables/filter_output.go122
-rw-r--r--test/iptables/iptables.go4
-rw-r--r--test/iptables/iptables_test.go95
-rw-r--r--test/iptables/iptables_unsafe.go63
-rw-r--r--test/iptables/iptables_util.go169
-rw-r--r--test/iptables/nat.go404
-rw-r--r--test/iptables/runner/main.go9
9 files changed, 817 insertions, 268 deletions
diff --git a/test/iptables/BUILD b/test/iptables/BUILD
index 3e29ca90d..66453772a 100644
--- a/test/iptables/BUILD
+++ b/test/iptables/BUILD
@@ -9,6 +9,7 @@ go_library(
"filter_input.go",
"filter_output.go",
"iptables.go",
+ "iptables_unsafe.go",
"iptables_util.go",
"nat.go",
],
@@ -20,6 +21,7 @@ go_library(
go_test(
name = "iptables_test",
+ size = "large",
srcs = [
"iptables_test.go",
],
diff --git a/test/iptables/filter_input.go b/test/iptables/filter_input.go
index 41e0cfa8d..5737ee317 100644
--- a/test/iptables/filter_input.go
+++ b/test/iptables/filter_input.go
@@ -25,7 +25,6 @@ const (
dropPort = 2401
acceptPort = 2402
sendloopDuration = 2 * time.Second
- network = "udp4"
chainName = "foochain"
)
@@ -49,6 +48,8 @@ func init() {
RegisterTestCase(FilterInputJumpTwice{})
RegisterTestCase(FilterInputDestination{})
RegisterTestCase(FilterInputInvertDestination{})
+ RegisterTestCase(FilterInputSource{})
+ RegisterTestCase(FilterInputInvertSource{})
}
// FilterInputDropUDP tests that we can drop UDP traffic.
@@ -60,8 +61,8 @@ func (FilterInputDropUDP) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputDropUDP) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil {
+func (FilterInputDropUDP) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil {
return err
}
@@ -78,8 +79,8 @@ func (FilterInputDropUDP) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputDropUDP) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, dropPort, sendloopDuration)
+func (FilterInputDropUDP) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, dropPort, sendloopDuration)
}
// FilterInputDropOnlyUDP tests that "-p udp -j DROP" only affects UDP traffic.
@@ -91,8 +92,8 @@ func (FilterInputDropOnlyUDP) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputDropOnlyUDP) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil {
+func (FilterInputDropOnlyUDP) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil {
return err
}
@@ -105,7 +106,7 @@ func (FilterInputDropOnlyUDP) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputDropOnlyUDP) LocalAction(ip net.IP) error {
+func (FilterInputDropOnlyUDP) LocalAction(ip net.IP, ipv6 bool) error {
// Try to establish a TCP connection with the container, which should
// succeed.
return connectTCP(ip, acceptPort, sendloopDuration)
@@ -120,8 +121,8 @@ func (FilterInputDropUDPPort) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputDropUDPPort) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
+func (FilterInputDropUDPPort) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
return err
}
@@ -138,8 +139,8 @@ func (FilterInputDropUDPPort) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputDropUDPPort) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, dropPort, sendloopDuration)
+func (FilterInputDropUDPPort) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, dropPort, sendloopDuration)
}
// FilterInputDropDifferentUDPPort tests that dropping traffic for a single UDP port
@@ -152,8 +153,8 @@ func (FilterInputDropDifferentUDPPort) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputDropDifferentUDPPort) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
+func (FilterInputDropDifferentUDPPort) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
return err
}
@@ -166,8 +167,8 @@ func (FilterInputDropDifferentUDPPort) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputDropDifferentUDPPort) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (FilterInputDropDifferentUDPPort) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// FilterInputDropTCPDestPort tests that connections are not accepted on specified source ports.
@@ -179,8 +180,8 @@ func (FilterInputDropTCPDestPort) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputDropTCPDestPort) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "INPUT", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
+func (FilterInputDropTCPDestPort) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "INPUT", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
return err
}
@@ -193,7 +194,7 @@ func (FilterInputDropTCPDestPort) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputDropTCPDestPort) LocalAction(ip net.IP) error {
+func (FilterInputDropTCPDestPort) LocalAction(ip net.IP, ipv6 bool) error {
// Ensure we cannot connect to the container.
for start := time.Now(); time.Since(start) < sendloopDuration; {
if err := connectTCP(ip, dropPort, sendloopDuration-time.Since(start)); err == nil {
@@ -213,9 +214,9 @@ func (FilterInputDropTCPSrcPort) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputDropTCPSrcPort) ContainerAction(ip net.IP) error {
+func (FilterInputDropTCPSrcPort) ContainerAction(ip net.IP, ipv6 bool) error {
// Drop anything from an ephemeral port.
- if err := filterTable("-A", "INPUT", "-p", "tcp", "-m", "tcp", "--sport", "1024:65535", "-j", "DROP"); err != nil {
+ if err := filterTable(ipv6, "-A", "INPUT", "-p", "tcp", "-m", "tcp", "--sport", "1024:65535", "-j", "DROP"); err != nil {
return err
}
@@ -228,7 +229,7 @@ func (FilterInputDropTCPSrcPort) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputDropTCPSrcPort) LocalAction(ip net.IP) error {
+func (FilterInputDropTCPSrcPort) LocalAction(ip net.IP, ipv6 bool) error {
// Ensure we cannot connect to the container.
for start := time.Now(); time.Since(start) < sendloopDuration; {
if err := connectTCP(ip, acceptPort, sendloopDuration-time.Since(start)); err == nil {
@@ -248,8 +249,8 @@ func (FilterInputDropAll) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputDropAll) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "INPUT", "-j", "DROP"); err != nil {
+func (FilterInputDropAll) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "INPUT", "-j", "DROP"); err != nil {
return err
}
@@ -266,8 +267,8 @@ func (FilterInputDropAll) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputDropAll) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, dropPort, sendloopDuration)
+func (FilterInputDropAll) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, dropPort, sendloopDuration)
}
// FilterInputMultiUDPRules verifies that multiple UDP rules are applied
@@ -282,17 +283,17 @@ func (FilterInputMultiUDPRules) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputMultiUDPRules) ContainerAction(ip net.IP) error {
+func (FilterInputMultiUDPRules) ContainerAction(ip net.IP, ipv6 bool) error {
rules := [][]string{
{"-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"},
{"-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", acceptPort), "-j", "ACCEPT"},
{"-L"},
}
- return filterTableRules(rules)
+ return filterTableRules(ipv6, rules)
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputMultiUDPRules) LocalAction(ip net.IP) error {
+func (FilterInputMultiUDPRules) LocalAction(ip net.IP, ipv6 bool) error {
// No-op.
return nil
}
@@ -307,14 +308,14 @@ func (FilterInputRequireProtocolUDP) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputRequireProtocolUDP) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "INPUT", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err == nil {
+func (FilterInputRequireProtocolUDP) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-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 {
+func (FilterInputRequireProtocolUDP) LocalAction(ip net.IP, ipv6 bool) error {
// No-op.
return nil
}
@@ -328,18 +329,18 @@ func (FilterInputCreateUserChain) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputCreateUserChain) ContainerAction(ip net.IP) error {
+func (FilterInputCreateUserChain) ContainerAction(ip net.IP, ipv6 bool) error {
rules := [][]string{
// Create a chain.
{"-N", chainName},
// Add a simple rule to the chain.
{"-A", chainName, "-j", "DROP"},
}
- return filterTableRules(rules)
+ return filterTableRules(ipv6, rules)
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputCreateUserChain) LocalAction(ip net.IP) error {
+func (FilterInputCreateUserChain) LocalAction(ip net.IP, ipv6 bool) error {
// No-op.
return nil
}
@@ -353,17 +354,17 @@ func (FilterInputDefaultPolicyAccept) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputDefaultPolicyAccept) ContainerAction(ip net.IP) error {
+func (FilterInputDefaultPolicyAccept) ContainerAction(ip net.IP, ipv6 bool) error {
// Set the default policy to accept, then receive a packet.
- if err := filterTable("-P", "INPUT", "ACCEPT"); err != nil {
+ if err := filterTable(ipv6, "-P", "INPUT", "ACCEPT"); err != nil {
return err
}
return listenUDP(acceptPort, sendloopDuration)
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputDefaultPolicyAccept) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (FilterInputDefaultPolicyAccept) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// FilterInputDefaultPolicyDrop tests the default DROP policy.
@@ -375,8 +376,8 @@ func (FilterInputDefaultPolicyDrop) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputDefaultPolicyDrop) ContainerAction(ip net.IP) error {
- if err := filterTable("-P", "INPUT", "DROP"); err != nil {
+func (FilterInputDefaultPolicyDrop) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-P", "INPUT", "DROP"); err != nil {
return err
}
@@ -393,8 +394,8 @@ func (FilterInputDefaultPolicyDrop) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputDefaultPolicyDrop) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (FilterInputDefaultPolicyDrop) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// FilterInputReturnUnderflow tests that -j RETURN in a built-in chain causes
@@ -407,7 +408,7 @@ func (FilterInputReturnUnderflow) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputReturnUnderflow) ContainerAction(ip net.IP) error {
+func (FilterInputReturnUnderflow) ContainerAction(ip net.IP, ipv6 bool) error {
// Add a RETURN rule followed by an unconditional accept, and set the
// default policy to DROP.
rules := [][]string{
@@ -415,7 +416,7 @@ func (FilterInputReturnUnderflow) ContainerAction(ip net.IP) error {
{"-A", "INPUT", "-j", "DROP"},
{"-P", "INPUT", "ACCEPT"},
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -425,8 +426,8 @@ func (FilterInputReturnUnderflow) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputReturnUnderflow) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (FilterInputReturnUnderflow) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// FilterInputSerializeJump verifies that we can serialize jumps.
@@ -438,18 +439,18 @@ func (FilterInputSerializeJump) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputSerializeJump) ContainerAction(ip net.IP) error {
+func (FilterInputSerializeJump) ContainerAction(ip net.IP, ipv6 bool) error {
// Write a JUMP rule, the serialize it with `-L`.
rules := [][]string{
{"-N", chainName},
{"-A", "INPUT", "-j", chainName},
{"-L"},
}
- return filterTableRules(rules)
+ return filterTableRules(ipv6, rules)
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputSerializeJump) LocalAction(ip net.IP) error {
+func (FilterInputSerializeJump) LocalAction(ip net.IP, ipv6 bool) error {
// No-op.
return nil
}
@@ -463,14 +464,14 @@ func (FilterInputJumpBasic) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputJumpBasic) ContainerAction(ip net.IP) error {
+func (FilterInputJumpBasic) ContainerAction(ip net.IP, ipv6 bool) error {
rules := [][]string{
{"-P", "INPUT", "DROP"},
{"-N", chainName},
{"-A", "INPUT", "-j", chainName},
{"-A", chainName, "-j", "ACCEPT"},
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -479,8 +480,8 @@ func (FilterInputJumpBasic) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputJumpBasic) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (FilterInputJumpBasic) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// FilterInputJumpReturn jumps, returns, and executes a rule.
@@ -492,7 +493,7 @@ func (FilterInputJumpReturn) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputJumpReturn) ContainerAction(ip net.IP) error {
+func (FilterInputJumpReturn) ContainerAction(ip net.IP, ipv6 bool) error {
rules := [][]string{
{"-N", chainName},
{"-P", "INPUT", "ACCEPT"},
@@ -500,7 +501,7 @@ func (FilterInputJumpReturn) ContainerAction(ip net.IP) error {
{"-A", chainName, "-j", "RETURN"},
{"-A", chainName, "-j", "DROP"},
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -509,8 +510,8 @@ func (FilterInputJumpReturn) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputJumpReturn) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (FilterInputJumpReturn) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// FilterInputJumpReturnDrop jumps to a chain, returns, and DROPs packets.
@@ -522,14 +523,14 @@ func (FilterInputJumpReturnDrop) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputJumpReturnDrop) ContainerAction(ip net.IP) error {
+func (FilterInputJumpReturnDrop) ContainerAction(ip net.IP, ipv6 bool) error {
rules := [][]string{
{"-N", chainName},
{"-A", "INPUT", "-j", chainName},
{"-A", "INPUT", "-j", "DROP"},
{"-A", chainName, "-j", "RETURN"},
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -546,8 +547,8 @@ func (FilterInputJumpReturnDrop) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputJumpReturnDrop) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, dropPort, sendloopDuration)
+func (FilterInputJumpReturnDrop) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, dropPort, sendloopDuration)
}
// FilterInputJumpBuiltin verifies that jumping to a top-levl chain is illegal.
@@ -559,15 +560,15 @@ func (FilterInputJumpBuiltin) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputJumpBuiltin) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "INPUT", "-j", "OUTPUT"); err == nil {
+func (FilterInputJumpBuiltin) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "INPUT", "-j", "OUTPUT"); err == nil {
return fmt.Errorf("iptables should be unable to jump to a built-in chain")
}
return nil
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputJumpBuiltin) LocalAction(ip net.IP) error {
+func (FilterInputJumpBuiltin) LocalAction(ip net.IP, ipv6 bool) error {
// No-op.
return nil
}
@@ -581,7 +582,7 @@ func (FilterInputJumpTwice) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputJumpTwice) ContainerAction(ip net.IP) error {
+func (FilterInputJumpTwice) ContainerAction(ip net.IP, ipv6 bool) error {
const chainName2 = chainName + "2"
rules := [][]string{
{"-P", "INPUT", "DROP"},
@@ -591,7 +592,7 @@ func (FilterInputJumpTwice) ContainerAction(ip net.IP) error {
{"-A", chainName, "-j", chainName2},
{"-A", "INPUT", "-j", "ACCEPT"},
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -601,8 +602,8 @@ func (FilterInputJumpTwice) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputJumpTwice) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (FilterInputJumpTwice) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// FilterInputDestination verifies that we can filter packets via `-d
@@ -615,8 +616,8 @@ func (FilterInputDestination) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputDestination) ContainerAction(ip net.IP) error {
- addrs, err := localAddrs()
+func (FilterInputDestination) ContainerAction(ip net.IP, ipv6 bool) error {
+ addrs, err := localAddrs(ipv6)
if err != nil {
return err
}
@@ -627,7 +628,7 @@ func (FilterInputDestination) ContainerAction(ip net.IP) error {
for _, addr := range addrs {
rules = append(rules, []string{"-A", "INPUT", "-d", addr, "-j", "ACCEPT"})
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -635,8 +636,8 @@ func (FilterInputDestination) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputDestination) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (FilterInputDestination) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// FilterInputInvertDestination verifies that we can filter packets via `! -d
@@ -649,14 +650,14 @@ func (FilterInputInvertDestination) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterInputInvertDestination) ContainerAction(ip net.IP) error {
+func (FilterInputInvertDestination) ContainerAction(ip net.IP, ipv6 bool) 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"},
+ {"-A", "INPUT", "!", "-d", localIP(ipv6), "-j", "ACCEPT"},
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -664,6 +665,64 @@ func (FilterInputInvertDestination) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterInputInvertDestination) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (FilterInputInvertDestination) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
+}
+
+// FilterInputSource verifies that we can filter packets via `-s
+// <ipaddr>`.
+type FilterInputSource struct{}
+
+// Name implements TestCase.Name.
+func (FilterInputSource) Name() string {
+ return "FilterInputSource"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterInputSource) ContainerAction(ip net.IP, ipv6 bool) error {
+ // Make INPUT's default action DROP, then ACCEPT all packets from this
+ // machine.
+ rules := [][]string{
+ {"-P", "INPUT", "DROP"},
+ {"-A", "INPUT", "-s", fmt.Sprintf("%v", ip), "-j", "ACCEPT"},
+ }
+ if err := filterTableRules(ipv6, rules); err != nil {
+ return err
+ }
+
+ return listenUDP(acceptPort, sendloopDuration)
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterInputSource) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
+}
+
+// FilterInputInvertSource verifies that we can filter packets via `! -s
+// <ipaddr>`.
+type FilterInputInvertSource struct{}
+
+// Name implements TestCase.Name.
+func (FilterInputInvertSource) Name() string {
+ return "FilterInputInvertSource"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterInputInvertSource) ContainerAction(ip net.IP, ipv6 bool) 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", "!", "-s", localIP(ipv6), "-j", "ACCEPT"},
+ }
+ if err := filterTableRules(ipv6, rules); err != nil {
+ return err
+ }
+
+ return listenUDP(acceptPort, sendloopDuration)
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterInputInvertSource) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
diff --git a/test/iptables/filter_output.go b/test/iptables/filter_output.go
index ba0d6fc29..c1d83b471 100644
--- a/test/iptables/filter_output.go
+++ b/test/iptables/filter_output.go
@@ -52,8 +52,8 @@ func (FilterOutputDropTCPDestPort) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputDropTCPDestPort) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--dport", "1024:65535", "-j", "DROP"); err != nil {
+func (FilterOutputDropTCPDestPort) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--dport", "1024:65535", "-j", "DROP"); err != nil {
return err
}
@@ -66,7 +66,7 @@ func (FilterOutputDropTCPDestPort) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputDropTCPDestPort) LocalAction(ip net.IP) error {
+func (FilterOutputDropTCPDestPort) LocalAction(ip net.IP, ipv6 bool) error {
if err := connectTCP(ip, acceptPort, sendloopDuration); err == nil {
return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort)
}
@@ -84,8 +84,8 @@ func (FilterOutputDropTCPSrcPort) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputDropTCPSrcPort) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--sport", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
+func (FilterOutputDropTCPSrcPort) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--sport", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
return err
}
@@ -98,7 +98,7 @@ func (FilterOutputDropTCPSrcPort) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputDropTCPSrcPort) LocalAction(ip net.IP) error {
+func (FilterOutputDropTCPSrcPort) LocalAction(ip net.IP, ipv6 bool) error {
if err := connectTCP(ip, dropPort, sendloopDuration); err == nil {
return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", dropPort)
}
@@ -115,8 +115,8 @@ func (FilterOutputAcceptTCPOwner) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputAcceptTCPOwner) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--uid-owner", "root", "-j", "ACCEPT"); err != nil {
+func (FilterOutputAcceptTCPOwner) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--uid-owner", "root", "-j", "ACCEPT"); err != nil {
return err
}
@@ -125,7 +125,7 @@ func (FilterOutputAcceptTCPOwner) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputAcceptTCPOwner) LocalAction(ip net.IP) error {
+func (FilterOutputAcceptTCPOwner) LocalAction(ip net.IP, ipv6 bool) error {
return connectTCP(ip, acceptPort, sendloopDuration)
}
@@ -138,8 +138,8 @@ func (FilterOutputDropTCPOwner) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputDropTCPOwner) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--uid-owner", "root", "-j", "DROP"); err != nil {
+func (FilterOutputDropTCPOwner) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--uid-owner", "root", "-j", "DROP"); err != nil {
return err
}
@@ -152,7 +152,7 @@ func (FilterOutputDropTCPOwner) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputDropTCPOwner) LocalAction(ip net.IP) error {
+func (FilterOutputDropTCPOwner) LocalAction(ip net.IP, ipv6 bool) error {
if err := connectTCP(ip, acceptPort, sendloopDuration); err == nil {
return fmt.Errorf("connection destined to port %d should be dropped, but got accepted", acceptPort)
}
@@ -169,8 +169,8 @@ func (FilterOutputAcceptUDPOwner) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputAcceptUDPOwner) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "udp", "-m", "owner", "--uid-owner", "root", "-j", "ACCEPT"); err != nil {
+func (FilterOutputAcceptUDPOwner) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-m", "owner", "--uid-owner", "root", "-j", "ACCEPT"); err != nil {
return err
}
@@ -179,7 +179,7 @@ func (FilterOutputAcceptUDPOwner) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputAcceptUDPOwner) LocalAction(ip net.IP) error {
+func (FilterOutputAcceptUDPOwner) LocalAction(ip net.IP, ipv6 bool) error {
// Listen for UDP packets on acceptPort.
return listenUDP(acceptPort, sendloopDuration)
}
@@ -193,8 +193,8 @@ func (FilterOutputDropUDPOwner) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputDropUDPOwner) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "udp", "-m", "owner", "--uid-owner", "root", "-j", "DROP"); err != nil {
+func (FilterOutputDropUDPOwner) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-m", "owner", "--uid-owner", "root", "-j", "DROP"); err != nil {
return err
}
@@ -203,7 +203,7 @@ func (FilterOutputDropUDPOwner) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputDropUDPOwner) LocalAction(ip net.IP) error {
+func (FilterOutputDropUDPOwner) LocalAction(ip net.IP, ipv6 bool) error {
// Listen for UDP packets on dropPort.
if err := listenUDP(dropPort, sendloopDuration); err == nil {
return fmt.Errorf("packets should not be received")
@@ -222,8 +222,8 @@ func (FilterOutputOwnerFail) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputOwnerFail) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "udp", "-m", "owner", "-j", "ACCEPT"); err == nil {
+func (FilterOutputOwnerFail) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-m", "owner", "-j", "ACCEPT"); err == nil {
return fmt.Errorf("Invalid argument")
}
@@ -231,7 +231,7 @@ func (FilterOutputOwnerFail) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputOwnerFail) LocalAction(ip net.IP) error {
+func (FilterOutputOwnerFail) LocalAction(ip net.IP, ipv6 bool) error {
// no-op.
return nil
}
@@ -245,8 +245,8 @@ func (FilterOutputAcceptGIDOwner) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputAcceptGIDOwner) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--gid-owner", "root", "-j", "ACCEPT"); err != nil {
+func (FilterOutputAcceptGIDOwner) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--gid-owner", "root", "-j", "ACCEPT"); err != nil {
return err
}
@@ -255,7 +255,7 @@ func (FilterOutputAcceptGIDOwner) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputAcceptGIDOwner) LocalAction(ip net.IP) error {
+func (FilterOutputAcceptGIDOwner) LocalAction(ip net.IP, ipv6 bool) error {
return connectTCP(ip, acceptPort, sendloopDuration)
}
@@ -268,8 +268,8 @@ func (FilterOutputDropGIDOwner) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputDropGIDOwner) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--gid-owner", "root", "-j", "DROP"); err != nil {
+func (FilterOutputDropGIDOwner) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--gid-owner", "root", "-j", "DROP"); err != nil {
return err
}
@@ -282,7 +282,7 @@ func (FilterOutputDropGIDOwner) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputDropGIDOwner) LocalAction(ip net.IP) error {
+func (FilterOutputDropGIDOwner) LocalAction(ip net.IP, ipv6 bool) error {
if err := connectTCP(ip, acceptPort, sendloopDuration); err == nil {
return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort)
}
@@ -299,12 +299,12 @@ func (FilterOutputInvertGIDOwner) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputInvertGIDOwner) ContainerAction(ip net.IP) error {
+func (FilterOutputInvertGIDOwner) ContainerAction(ip net.IP, ipv6 bool) error {
rules := [][]string{
{"-A", "OUTPUT", "-p", "tcp", "-m", "owner", "!", "--gid-owner", "root", "-j", "ACCEPT"},
{"-A", "OUTPUT", "-p", "tcp", "-j", "DROP"},
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -317,7 +317,7 @@ func (FilterOutputInvertGIDOwner) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputInvertGIDOwner) LocalAction(ip net.IP) error {
+func (FilterOutputInvertGIDOwner) LocalAction(ip net.IP, ipv6 bool) error {
if err := connectTCP(ip, acceptPort, sendloopDuration); err == nil {
return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort)
}
@@ -334,12 +334,12 @@ func (FilterOutputInvertUIDOwner) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputInvertUIDOwner) ContainerAction(ip net.IP) error {
+func (FilterOutputInvertUIDOwner) ContainerAction(ip net.IP, ipv6 bool) error {
rules := [][]string{
{"-A", "OUTPUT", "-p", "tcp", "-m", "owner", "!", "--uid-owner", "root", "-j", "DROP"},
{"-A", "OUTPUT", "-p", "tcp", "-j", "ACCEPT"},
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -348,7 +348,7 @@ func (FilterOutputInvertUIDOwner) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputInvertUIDOwner) LocalAction(ip net.IP) error {
+func (FilterOutputInvertUIDOwner) LocalAction(ip net.IP, ipv6 bool) error {
return connectTCP(ip, acceptPort, sendloopDuration)
}
@@ -362,12 +362,12 @@ func (FilterOutputInvertUIDAndGIDOwner) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputInvertUIDAndGIDOwner) ContainerAction(ip net.IP) error {
+func (FilterOutputInvertUIDAndGIDOwner) ContainerAction(ip net.IP, ipv6 bool) error {
rules := [][]string{
{"-A", "OUTPUT", "-p", "tcp", "-m", "owner", "!", "--uid-owner", "root", "!", "--gid-owner", "root", "-j", "ACCEPT"},
{"-A", "OUTPUT", "-p", "tcp", "-j", "DROP"},
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -380,7 +380,7 @@ func (FilterOutputInvertUIDAndGIDOwner) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputInvertUIDAndGIDOwner) LocalAction(ip net.IP) error {
+func (FilterOutputInvertUIDAndGIDOwner) LocalAction(ip net.IP, ipv6 bool) error {
if err := connectTCP(ip, acceptPort, sendloopDuration); err == nil {
return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort)
}
@@ -398,12 +398,12 @@ func (FilterOutputDestination) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputDestination) ContainerAction(ip net.IP) error {
+func (FilterOutputDestination) ContainerAction(ip net.IP, ipv6 bool) error {
rules := [][]string{
{"-A", "OUTPUT", "-d", ip.String(), "-j", "ACCEPT"},
{"-P", "OUTPUT", "DROP"},
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -411,7 +411,7 @@ func (FilterOutputDestination) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputDestination) LocalAction(ip net.IP) error {
+func (FilterOutputDestination) LocalAction(ip net.IP, ipv6 bool) error {
return listenUDP(acceptPort, sendloopDuration)
}
@@ -425,12 +425,12 @@ func (FilterOutputInvertDestination) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputInvertDestination) ContainerAction(ip net.IP) error {
+func (FilterOutputInvertDestination) ContainerAction(ip net.IP, ipv6 bool) error {
rules := [][]string{
- {"-A", "OUTPUT", "!", "-d", localIP, "-j", "ACCEPT"},
+ {"-A", "OUTPUT", "!", "-d", localIP(ipv6), "-j", "ACCEPT"},
{"-P", "OUTPUT", "DROP"},
}
- if err := filterTableRules(rules); err != nil {
+ if err := filterTableRules(ipv6, rules); err != nil {
return err
}
@@ -438,7 +438,7 @@ func (FilterOutputInvertDestination) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputInvertDestination) LocalAction(ip net.IP) error {
+func (FilterOutputInvertDestination) LocalAction(ip net.IP, ipv6 bool) error {
return listenUDP(acceptPort, sendloopDuration)
}
@@ -452,12 +452,12 @@ func (FilterOutputInterfaceAccept) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputInterfaceAccept) ContainerAction(ip net.IP) error {
+func (FilterOutputInterfaceAccept) ContainerAction(ip net.IP, ipv6 bool) error {
ifname, ok := getInterfaceName()
if !ok {
return fmt.Errorf("no interface is present, except loopback")
}
- if err := filterTable("-A", "OUTPUT", "-p", "udp", "-o", ifname, "-j", "ACCEPT"); err != nil {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", ifname, "-j", "ACCEPT"); err != nil {
return err
}
@@ -465,7 +465,7 @@ func (FilterOutputInterfaceAccept) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputInterfaceAccept) LocalAction(ip net.IP) error {
+func (FilterOutputInterfaceAccept) LocalAction(ip net.IP, ipv6 bool) error {
return listenUDP(acceptPort, sendloopDuration)
}
@@ -479,12 +479,12 @@ func (FilterOutputInterfaceDrop) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputInterfaceDrop) ContainerAction(ip net.IP) error {
+func (FilterOutputInterfaceDrop) ContainerAction(ip net.IP, ipv6 bool) error {
ifname, ok := getInterfaceName()
if !ok {
return fmt.Errorf("no interface is present, except loopback")
}
- if err := filterTable("-A", "OUTPUT", "-p", "udp", "-o", ifname, "-j", "DROP"); err != nil {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", ifname, "-j", "DROP"); err != nil {
return err
}
@@ -492,7 +492,7 @@ func (FilterOutputInterfaceDrop) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputInterfaceDrop) LocalAction(ip net.IP) error {
+func (FilterOutputInterfaceDrop) LocalAction(ip net.IP, ipv6 bool) error {
if err := listenUDP(acceptPort, sendloopDuration); err == nil {
return fmt.Errorf("packets should not be received on port %v, but are received", acceptPort)
}
@@ -510,8 +510,8 @@ func (FilterOutputInterface) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputInterface) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "udp", "-o", "lo", "-j", "DROP"); err != nil {
+func (FilterOutputInterface) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", "lo", "-j", "DROP"); err != nil {
return err
}
@@ -519,7 +519,7 @@ func (FilterOutputInterface) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputInterface) LocalAction(ip net.IP) error {
+func (FilterOutputInterface) LocalAction(ip net.IP, ipv6 bool) error {
return listenUDP(acceptPort, sendloopDuration)
}
@@ -533,8 +533,8 @@ func (FilterOutputInterfaceBeginsWith) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputInterfaceBeginsWith) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "udp", "-o", "e+", "-j", "DROP"); err != nil {
+func (FilterOutputInterfaceBeginsWith) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", "e+", "-j", "DROP"); err != nil {
return err
}
@@ -542,7 +542,7 @@ func (FilterOutputInterfaceBeginsWith) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputInterfaceBeginsWith) LocalAction(ip net.IP) error {
+func (FilterOutputInterfaceBeginsWith) LocalAction(ip net.IP, ipv6 bool) error {
if err := listenUDP(acceptPort, sendloopDuration); err == nil {
return fmt.Errorf("packets should not be received on port %v, but are received", acceptPort)
}
@@ -560,8 +560,8 @@ func (FilterOutputInterfaceInvertDrop) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputInterfaceInvertDrop) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "tcp", "!", "-o", "lo", "-j", "DROP"); err != nil {
+func (FilterOutputInterfaceInvertDrop) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "!", "-o", "lo", "-j", "DROP"); err != nil {
return err
}
@@ -574,7 +574,7 @@ func (FilterOutputInterfaceInvertDrop) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputInterfaceInvertDrop) LocalAction(ip net.IP) error {
+func (FilterOutputInterfaceInvertDrop) LocalAction(ip net.IP, ipv6 bool) error {
if err := connectTCP(ip, acceptPort, sendloopDuration); err == nil {
return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort)
}
@@ -592,8 +592,8 @@ func (FilterOutputInterfaceInvertAccept) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (FilterOutputInterfaceInvertAccept) ContainerAction(ip net.IP) error {
- if err := filterTable("-A", "OUTPUT", "-p", "tcp", "!", "-o", "lo", "-j", "ACCEPT"); err != nil {
+func (FilterOutputInterfaceInvertAccept) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "!", "-o", "lo", "-j", "ACCEPT"); err != nil {
return err
}
@@ -602,6 +602,6 @@ func (FilterOutputInterfaceInvertAccept) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (FilterOutputInterfaceInvertAccept) LocalAction(ip net.IP) error {
+func (FilterOutputInterfaceInvertAccept) LocalAction(ip net.IP, ipv6 bool) error {
return connectTCP(ip, acceptPort, sendloopDuration)
}
diff --git a/test/iptables/iptables.go b/test/iptables/iptables.go
index 16cb4f4da..dfbd80cd1 100644
--- a/test/iptables/iptables.go
+++ b/test/iptables/iptables.go
@@ -40,10 +40,10 @@ type TestCase interface {
// ContainerAction runs inside the container. It receives the IP of the
// local process.
- ContainerAction(ip net.IP) error
+ ContainerAction(ip net.IP, ipv6 bool) error
// LocalAction runs locally. It receives the IP of the container.
- LocalAction(ip net.IP) error
+ LocalAction(ip net.IP, ipv6 bool) error
}
// Tests maps test names to TestCase.
diff --git a/test/iptables/iptables_test.go b/test/iptables/iptables_test.go
index 4fd2cb46a..fda5f694f 100644
--- a/test/iptables/iptables_test.go
+++ b/test/iptables/iptables_test.go
@@ -15,8 +15,10 @@
package iptables
import (
+ "context"
"fmt"
"net"
+ "reflect"
"testing"
"gvisor.dev/gvisor/pkg/test/dockerutil"
@@ -33,24 +35,47 @@ import (
// Container output is logged to $TEST_UNDECLARED_OUTPUTS_DIR if it exists, or
// to stderr.
func singleTest(t *testing.T, test TestCase) {
+ for _, tc := range []bool{false, true} {
+ subtest := "IPv4"
+ if tc {
+ subtest = "IPv6"
+ }
+ t.Run(subtest, func(t *testing.T) {
+ iptablesTest(t, test, tc)
+ })
+ }
+}
+
+func iptablesTest(t *testing.T, test TestCase, ipv6 bool) {
if _, ok := Tests[test.Name()]; !ok {
t.Fatalf("no test found with name %q. Has it been registered?", test.Name())
}
- d := dockerutil.MakeDocker(t)
- defer d.CleanUp()
+ ctx := context.Background()
+ d := dockerutil.MakeContainer(ctx, t)
+ defer d.CleanUp(ctx)
+
+ // TODO(gvisor.dev/issue/170): Skipping IPv6 gVisor tests.
+ if ipv6 && dockerutil.Runtime() != "runc" {
+ t.Skip("gVisor ip6tables not yet implemented")
+ }
// Create and start the container.
- d.CopyFiles("/runner", "test/iptables/runner/runner")
- if err := d.Spawn(dockerutil.RunOpts{
+ opts := dockerutil.RunOpts{
Image: "iptables",
CapAdd: []string{"NET_ADMIN"},
- }, "/runner/runner", "-name", test.Name()); err != nil {
+ }
+ d.CopyFiles(&opts, "/runner", "test/iptables/runner/runner")
+ args := []string{"/runner/runner", "-name", test.Name()}
+ if ipv6 {
+ args = append(args, "-ipv6")
+ }
+ if err := d.Spawn(ctx, opts, args...); err != nil {
t.Fatalf("docker run failed: %v", err)
}
// Get the container IP.
- ip, err := d.FindIP()
+ ip, err := d.FindIP(ctx, ipv6)
if err != nil {
t.Fatalf("failed to get container IP: %v", err)
}
@@ -61,14 +86,14 @@ func singleTest(t *testing.T, test TestCase) {
}
// Run our side of the test.
- if err := test.LocalAction(ip); err != nil {
+ if err := test.LocalAction(ip, ipv6); err != nil {
t.Fatalf("LocalAction failed: %v", err)
}
// Wait for the final statement. This structure has the side effect
// that all container logs will appear within the individual test
// context.
- if _, err := d.WaitForOutput(TerminalStatement, TestTimeout); err != nil {
+ if _, err := d.WaitForOutput(ctx, TerminalStatement, TestTimeout); err != nil {
t.Fatalf("test failed: %v", err)
}
}
@@ -82,7 +107,7 @@ func sendIP(ip net.IP) error {
// The container may not be listening when we first connect, so retry
// upon error.
cb := func() error {
- c, err := net.DialTCP("tcp4", nil, &contAddr)
+ c, err := net.DialTCP("tcp", nil, &contAddr)
conn = c
return err
}
@@ -259,6 +284,13 @@ func TestNATPreRedirectTCPPort(t *testing.T) {
singleTest(t, NATPreRedirectTCPPort{})
}
+func TestNATPreRedirectTCPOutgoing(t *testing.T) {
+ singleTest(t, NATPreRedirectTCPOutgoing{})
+}
+
+func TestNATOutRedirectTCPIncoming(t *testing.T) {
+ singleTest(t, NATOutRedirectTCPIncoming{})
+}
func TestNATOutRedirectUDPPort(t *testing.T) {
singleTest(t, NATOutRedirectUDPPort{})
}
@@ -302,3 +334,48 @@ func TestNATPreRedirectInvert(t *testing.T) {
func TestNATRedirectRequiresProtocol(t *testing.T) {
singleTest(t, NATRedirectRequiresProtocol{})
}
+
+func TestNATLoopbackSkipsPrerouting(t *testing.T) {
+ singleTest(t, NATLoopbackSkipsPrerouting{})
+}
+
+func TestInputSource(t *testing.T) {
+ singleTest(t, FilterInputSource{})
+}
+
+func TestInputInvertSource(t *testing.T) {
+ singleTest(t, FilterInputInvertSource{})
+}
+
+func TestFilterAddrs(t *testing.T) {
+ tcs := []struct {
+ ipv6 bool
+ addrs []string
+ want []string
+ }{
+ {
+ ipv6: false,
+ addrs: []string{"192.168.0.1", "192.168.0.2/24", "::1", "::2/128"},
+ want: []string{"192.168.0.1", "192.168.0.2"},
+ },
+ {
+ ipv6: true,
+ addrs: []string{"192.168.0.1", "192.168.0.2/24", "::1", "::2/128"},
+ want: []string{"::1", "::2"},
+ },
+ }
+
+ for _, tc := range tcs {
+ if got := filterAddrs(tc.addrs, tc.ipv6); !reflect.DeepEqual(got, tc.want) {
+ t.Errorf("%v with IPv6 %t: got %v, but wanted %v", tc.addrs, tc.ipv6, got, tc.want)
+ }
+ }
+}
+
+func TestNATPreOriginalDst(t *testing.T) {
+ singleTest(t, NATPreOriginalDst{})
+}
+
+func TestNATOutOriginalDst(t *testing.T) {
+ singleTest(t, NATOutOriginalDst{})
+}
diff --git a/test/iptables/iptables_unsafe.go b/test/iptables/iptables_unsafe.go
new file mode 100644
index 000000000..bd85a8fea
--- /dev/null
+++ b/test/iptables/iptables_unsafe.go
@@ -0,0 +1,63 @@
+// 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"
+ "syscall"
+ "unsafe"
+)
+
+type originalDstError struct {
+ errno syscall.Errno
+}
+
+func (e originalDstError) Error() string {
+ return fmt.Sprintf("errno (%d) when calling getsockopt(SO_ORIGINAL_DST): %v", int(e.errno), e.errno.Error())
+}
+
+// SO_ORIGINAL_DST gets the original destination of a redirected packet via
+// getsockopt.
+const SO_ORIGINAL_DST = 80
+
+func originalDestination4(connfd int) (syscall.RawSockaddrInet4, error) {
+ var addr syscall.RawSockaddrInet4
+ var addrLen uint32 = syscall.SizeofSockaddrInet4
+ if errno := originalDestination(connfd, syscall.SOL_IP, unsafe.Pointer(&addr), &addrLen); errno != 0 {
+ return syscall.RawSockaddrInet4{}, originalDstError{errno}
+ }
+ return addr, nil
+}
+
+func originalDestination6(connfd int) (syscall.RawSockaddrInet6, error) {
+ var addr syscall.RawSockaddrInet6
+ var addrLen uint32 = syscall.SizeofSockaddrInet6
+ if errno := originalDestination(connfd, syscall.SOL_IPV6, unsafe.Pointer(&addr), &addrLen); errno != 0 {
+ return syscall.RawSockaddrInet6{}, originalDstError{errno}
+ }
+ return addr, nil
+}
+
+func originalDestination(connfd int, level uintptr, optval unsafe.Pointer, optlen *uint32) syscall.Errno {
+ _, _, errno := syscall.Syscall6(
+ syscall.SYS_GETSOCKOPT,
+ uintptr(connfd),
+ level,
+ SO_ORIGINAL_DST,
+ uintptr(optval),
+ uintptr(unsafe.Pointer(optlen)),
+ 0)
+ return errno
+}
diff --git a/test/iptables/iptables_util.go b/test/iptables/iptables_util.go
index 7146edbb9..5125fe47b 100644
--- a/test/iptables/iptables_util.go
+++ b/test/iptables/iptables_util.go
@@ -15,30 +15,34 @@
package iptables
import (
+ "encoding/binary"
+ "errors"
"fmt"
"net"
"os/exec"
+ "strings"
"time"
"gvisor.dev/gvisor/pkg/test/testutil"
)
-const iptablesBinary = "iptables"
-const localIP = "127.0.0.1"
-
-// filterTable calls `iptables -t filter` with the given args.
-func filterTable(args ...string) error {
- return tableCmd("filter", args)
+// filterTable calls `ip{6}tables -t filter` with the given args.
+func filterTable(ipv6 bool, args ...string) error {
+ return tableCmd(ipv6, "filter", args)
}
-// natTable calls `iptables -t nat` with the given args.
-func natTable(args ...string) error {
- return tableCmd("nat", args)
+// natTable calls `ip{6}tables -t nat` with the given args.
+func natTable(ipv6 bool, args ...string) error {
+ return tableCmd(ipv6, "nat", args)
}
-func tableCmd(table string, args []string) error {
+func tableCmd(ipv6 bool, table string, args []string) error {
args = append([]string{"-t", table}, args...)
- cmd := exec.Command(iptablesBinary, args...)
+ binary := "iptables"
+ if ipv6 {
+ binary = "ip6tables"
+ }
+ cmd := exec.Command(binary, args...)
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("error running iptables with args %v\nerror: %v\noutput: %s", args, err, string(out))
}
@@ -46,18 +50,18 @@ 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)
+func filterTableRules(ipv6 bool, argsList [][]string) error {
+ return tableRules(ipv6, "filter", argsList)
}
// natTableRules is like natTable, but runs multiple iptables commands.
-func natTableRules(argsList [][]string) error {
- return tableRules("nat", argsList)
+func natTableRules(ipv6 bool, argsList [][]string) error {
+ return tableRules(ipv6, "nat", argsList)
}
-func tableRules(table string, argsList [][]string) error {
+func tableRules(ipv6 bool, table string, argsList [][]string) error {
for _, args := range argsList {
- if err := tableCmd(table, args); err != nil {
+ if err := tableCmd(ipv6, table, args); err != nil {
return err
}
}
@@ -70,7 +74,7 @@ func listenUDP(port int, timeout time.Duration) error {
localAddr := net.UDPAddr{
Port: port,
}
- conn, err := net.ListenUDP(network, &localAddr)
+ conn, err := net.ListenUDP("udp", &localAddr)
if err != nil {
return err
}
@@ -83,17 +87,42 @@ func listenUDP(port int, timeout time.Duration) error {
// sendUDPLoop sends 1 byte UDP packets repeatedly to the IP and port specified
// over a duration.
func sendUDPLoop(ip net.IP, port int, duration time.Duration) error {
- // Send packets for a few seconds.
+ conn, err := connectUDP(ip, port)
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+ loopUDP(conn, duration)
+ return nil
+}
+
+// spawnUDPLoop works like sendUDPLoop, but returns immediately and sends
+// packets in another goroutine.
+func spawnUDPLoop(ip net.IP, port int, duration time.Duration) error {
+ conn, err := connectUDP(ip, port)
+ if err != nil {
+ return err
+ }
+ go func() {
+ defer conn.Close()
+ loopUDP(conn, duration)
+ }()
+ return nil
+}
+
+func connectUDP(ip net.IP, port int) (net.Conn, error) {
remote := net.UDPAddr{
IP: ip,
Port: port,
}
- conn, err := net.DialUDP(network, nil, &remote)
+ conn, err := net.DialUDP("udp", nil, &remote)
if err != nil {
- return err
+ return nil, err
}
- defer conn.Close()
+ return conn, nil
+}
+func loopUDP(conn net.Conn, duration time.Duration) {
to := time.After(duration)
for timedOut := false; !timedOut; {
// This may return an error (connection refused) if the remote
@@ -108,8 +137,6 @@ func sendUDPLoop(ip net.IP, port int, duration time.Duration) error {
time.Sleep(200 * time.Millisecond)
}
}
-
- return nil
}
// listenTCP listens for connections on a TCP port.
@@ -119,7 +146,7 @@ func listenTCP(port int, timeout time.Duration) error {
}
// Starts listening on port.
- lConn, err := net.ListenTCP("tcp4", &localAddr)
+ lConn, err := net.ListenTCP("tcp", &localAddr)
if err != nil {
return err
}
@@ -157,30 +184,106 @@ 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) {
+// localAddrs returns a list of local network interface addresses. When ipv6 is
+// true, only IPv6 addresses are returned. Otherwise only IPv4 addresses are
+// returned.
+func localAddrs(ipv6 bool) ([]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())
+ // Add only IPv4 or only IPv6 addresses.
+ parts := strings.Split(addr.String(), "/")
+ if len(parts) != 2 {
+ return nil, fmt.Errorf("bad interface address: %q", addr.String())
+ }
+ if isIPv6 := net.ParseIP(parts[0]).To4() == nil; isIPv6 == ipv6 {
+ addrStrs = append(addrStrs, addr.String())
+ }
+ }
+ return filterAddrs(addrStrs, ipv6), nil
+}
+
+func filterAddrs(addrs []string, ipv6 bool) []string {
+ addrStrs := make([]string, 0, len(addrs))
+ for _, addr := range addrs {
+ // Add only IPv4 or only IPv6 addresses.
+ parts := strings.Split(addr, "/")
+ if isIPv6 := net.ParseIP(parts[0]).To4() == nil; isIPv6 == ipv6 {
+ addrStrs = append(addrStrs, parts[0])
+ }
}
- return addrStrs, nil
+ return addrStrs
}
// getInterfaceName returns the name of the interface other than loopback.
func getInterfaceName() (string, bool) {
- var ifname string
+ iface, ok := getNonLoopbackInterface()
+ if !ok {
+ return "", false
+ }
+ return iface.Name, true
+}
+
+func getInterfaceAddrs(ipv6 bool) ([]net.IP, error) {
+ iface, ok := getNonLoopbackInterface()
+ if !ok {
+ return nil, errors.New("no non-loopback interface found")
+ }
+ addrs, err := iface.Addrs()
+ if err != nil {
+ return nil, err
+ }
+
+ // Get only IPv4 or IPv6 addresses.
+ ips := make([]net.IP, 0, len(addrs))
+ for _, addr := range addrs {
+ parts := strings.Split(addr.String(), "/")
+ var ip net.IP
+ // To16() returns IPv4 addresses as IPv4-mapped IPv6 addresses.
+ // So we check whether To4() returns nil to test whether the
+ // address is v4 or v6.
+ if v4 := net.ParseIP(parts[0]).To4(); ipv6 && v4 == nil {
+ ip = net.ParseIP(parts[0]).To16()
+ } else {
+ ip = v4
+ }
+ if ip != nil {
+ ips = append(ips, ip)
+ }
+ }
+ return ips, nil
+}
+
+func getNonLoopbackInterface() (net.Interface, bool) {
if interfaces, err := net.Interfaces(); err == nil {
for _, intf := range interfaces {
if intf.Name != "lo" {
- ifname = intf.Name
- break
+ return intf, true
}
}
}
+ return net.Interface{}, false
+}
+
+func htons(x uint16) uint16 {
+ buf := make([]byte, 2)
+ binary.BigEndian.PutUint16(buf, x)
+ return binary.LittleEndian.Uint16(buf)
+}
+
+func localIP(ipv6 bool) string {
+ if ipv6 {
+ return "::1"
+ }
+ return "127.0.0.1"
+}
- return ifname, ifname != ""
+func nowhereIP(ipv6 bool) string {
+ if ipv6 {
+ return "2001:db8::1"
+ }
+ return "192.0.2.1"
}
diff --git a/test/iptables/nat.go b/test/iptables/nat.go
index 0a10ce7fe..b7fea2527 100644
--- a/test/iptables/nat.go
+++ b/test/iptables/nat.go
@@ -18,16 +18,17 @@ import (
"errors"
"fmt"
"net"
+ "syscall"
"time"
)
-const (
- redirectPort = 42
-)
+const redirectPort = 42
func init() {
RegisterTestCase(NATPreRedirectUDPPort{})
RegisterTestCase(NATPreRedirectTCPPort{})
+ RegisterTestCase(NATPreRedirectTCPOutgoing{})
+ RegisterTestCase(NATOutRedirectTCPIncoming{})
RegisterTestCase(NATOutRedirectUDPPort{})
RegisterTestCase(NATOutRedirectTCPPort{})
RegisterTestCase(NATDropUDP{})
@@ -39,6 +40,9 @@ func init() {
RegisterTestCase(NATOutDontRedirectIP{})
RegisterTestCase(NATOutRedirectInvert{})
RegisterTestCase(NATRedirectRequiresProtocol{})
+ RegisterTestCase(NATLoopbackSkipsPrerouting{})
+ RegisterTestCase(NATPreOriginalDst{})
+ RegisterTestCase(NATOutOriginalDst{})
}
// NATPreRedirectUDPPort tests that packets are redirected to different port.
@@ -50,8 +54,8 @@ func (NATPreRedirectUDPPort) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (NATPreRedirectUDPPort) ContainerAction(ip net.IP) error {
- if err := natTable("-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
+func (NATPreRedirectUDPPort) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
return err
}
@@ -63,8 +67,8 @@ func (NATPreRedirectUDPPort) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (NATPreRedirectUDPPort) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (NATPreRedirectUDPPort) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// NATPreRedirectTCPPort tests that connections are redirected on specified ports.
@@ -76,8 +80,8 @@ func (NATPreRedirectTCPPort) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (NATPreRedirectTCPPort) ContainerAction(ip net.IP) error {
- if err := natTable("-A", "PREROUTING", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err != nil {
+func (NATPreRedirectTCPPort) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := natTable(ipv6, "-A", "PREROUTING", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err != nil {
return err
}
@@ -86,10 +90,60 @@ func (NATPreRedirectTCPPort) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (NATPreRedirectTCPPort) LocalAction(ip net.IP) error {
+func (NATPreRedirectTCPPort) LocalAction(ip net.IP, ipv6 bool) error {
return connectTCP(ip, dropPort, sendloopDuration)
}
+// NATPreRedirectTCPOutgoing verifies that outgoing TCP connections aren't
+// affected by PREROUTING connection tracking.
+type NATPreRedirectTCPOutgoing struct{}
+
+// Name implements TestCase.Name.
+func (NATPreRedirectTCPOutgoing) Name() string {
+ return "NATPreRedirectTCPOutgoing"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (NATPreRedirectTCPOutgoing) ContainerAction(ip net.IP, ipv6 bool) error {
+ // Redirect all incoming TCP traffic to a closed port.
+ if err := natTable(ipv6, "-A", "PREROUTING", "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", dropPort)); err != nil {
+ return err
+ }
+
+ // Establish a connection to the host process.
+ return connectTCP(ip, acceptPort, sendloopDuration)
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (NATPreRedirectTCPOutgoing) LocalAction(ip net.IP, ipv6 bool) error {
+ return listenTCP(acceptPort, sendloopDuration)
+}
+
+// NATOutRedirectTCPIncoming verifies that incoming TCP connections aren't
+// affected by OUTPUT connection tracking.
+type NATOutRedirectTCPIncoming struct{}
+
+// Name implements TestCase.Name.
+func (NATOutRedirectTCPIncoming) Name() string {
+ return "NATOutRedirectTCPIncoming"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (NATOutRedirectTCPIncoming) ContainerAction(ip net.IP, ipv6 bool) error {
+ // Redirect all outgoing TCP traffic to a closed port.
+ if err := natTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", dropPort)); err != nil {
+ return err
+ }
+
+ // Establish a connection to the host process.
+ return listenTCP(acceptPort, sendloopDuration)
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (NATOutRedirectTCPIncoming) LocalAction(ip net.IP, ipv6 bool) error {
+ return connectTCP(ip, acceptPort, sendloopDuration)
+}
+
// NATOutRedirectUDPPort tests that packets are redirected to different port.
type NATOutRedirectUDPPort struct{}
@@ -99,13 +153,12 @@ func (NATOutRedirectUDPPort) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (NATOutRedirectUDPPort) ContainerAction(ip net.IP) error {
- dest := []byte{200, 0, 0, 1}
- return loopbackTest(dest, "-A", "OUTPUT", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort))
+func (NATOutRedirectUDPPort) ContainerAction(ip net.IP, ipv6 bool) error {
+ return loopbackTest(ipv6, net.ParseIP(nowhereIP(ipv6)), "-A", "OUTPUT", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort))
}
// LocalAction implements TestCase.LocalAction.
-func (NATOutRedirectUDPPort) LocalAction(ip net.IP) error {
+func (NATOutRedirectUDPPort) LocalAction(ip net.IP, ipv6 bool) error {
// No-op.
return nil
}
@@ -120,8 +173,8 @@ func (NATDropUDP) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (NATDropUDP) ContainerAction(ip net.IP) error {
- if err := natTable("-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
+func (NATDropUDP) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
return err
}
@@ -133,8 +186,8 @@ func (NATDropUDP) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (NATDropUDP) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (NATDropUDP) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// NATAcceptAll tests that all UDP packets are accepted.
@@ -146,8 +199,8 @@ func (NATAcceptAll) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (NATAcceptAll) ContainerAction(ip net.IP) error {
- if err := natTable("-A", "PREROUTING", "-p", "udp", "-j", "ACCEPT"); err != nil {
+func (NATAcceptAll) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "ACCEPT"); err != nil {
return err
}
@@ -159,8 +212,8 @@ func (NATAcceptAll) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (NATAcceptAll) LocalAction(ip net.IP) error {
- return sendUDPLoop(ip, acceptPort, sendloopDuration)
+func (NATAcceptAll) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// NATOutRedirectIP uses iptables to select packets based on destination IP and
@@ -173,14 +226,17 @@ func (NATOutRedirectIP) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (NATOutRedirectIP) ContainerAction(ip net.IP) error {
+func (NATOutRedirectIP) ContainerAction(ip net.IP, ipv6 bool) 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))
+ return loopbackTest(ipv6, net.ParseIP(nowhereIP(ipv6)),
+ "-A", "OUTPUT",
+ "-d", nowhereIP(ipv6),
+ "-p", "udp",
+ "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort))
}
// LocalAction implements TestCase.LocalAction.
-func (NATOutRedirectIP) LocalAction(ip net.IP) error {
+func (NATOutRedirectIP) LocalAction(ip net.IP, ipv6 bool) error {
// No-op.
return nil
}
@@ -195,15 +251,15 @@ func (NATOutDontRedirectIP) Name() string {
}
// 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 {
+func (NATOutDontRedirectIP) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := natTable(ipv6, "-A", "OUTPUT", "-d", localIP(ipv6), "-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 {
+func (NATOutDontRedirectIP) LocalAction(ip net.IP, ipv6 bool) error {
return listenUDP(acceptPort, sendloopDuration)
}
@@ -216,15 +272,21 @@ func (NATOutRedirectInvert) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (NATOutRedirectInvert) ContainerAction(ip net.IP) error {
+func (NATOutRedirectInvert) ContainerAction(ip net.IP, ipv6 bool) 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))
+ dest := "192.0.2.2"
+ if ipv6 {
+ dest = "2001:db8::2"
+ }
+ return loopbackTest(ipv6, net.ParseIP(nowhereIP(ipv6)),
+ "-A", "OUTPUT",
+ "!", "-d", dest,
+ "-p", "udp",
+ "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort))
}
// LocalAction implements TestCase.LocalAction.
-func (NATOutRedirectInvert) LocalAction(ip net.IP) error {
+func (NATOutRedirectInvert) LocalAction(ip net.IP, ipv6 bool) error {
// No-op.
return nil
}
@@ -239,8 +301,8 @@ func (NATPreRedirectIP) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (NATPreRedirectIP) ContainerAction(ip net.IP) error {
- addrs, err := localAddrs()
+func (NATPreRedirectIP) ContainerAction(ip net.IP, ipv6 bool) error {
+ addrs, err := localAddrs(ipv6)
if err != nil {
return err
}
@@ -249,15 +311,15 @@ func (NATPreRedirectIP) ContainerAction(ip net.IP) error {
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 {
+ if err := natTableRules(ipv6, 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)
+func (NATPreRedirectIP) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, dropPort, sendloopDuration)
}
// NATPreDontRedirectIP tests that iptables matching with "-d" does not match
@@ -270,16 +332,16 @@ func (NATPreDontRedirectIP) Name() string {
}
// 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 {
+func (NATPreDontRedirectIP) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-d", localIP(ipv6), "-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)
+func (NATPreDontRedirectIP) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, acceptPort, sendloopDuration)
}
// NATPreRedirectInvert tests that iptables can match with "! -d".
@@ -291,16 +353,16 @@ func (NATPreRedirectInvert) Name() string {
}
// 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 {
+func (NATPreRedirectInvert) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "!", "-d", localIP(ipv6), "-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)
+func (NATPreRedirectInvert) LocalAction(ip net.IP, ipv6 bool) error {
+ return spawnUDPLoop(ip, dropPort, sendloopDuration)
}
// NATRedirectRequiresProtocol tests that use of the --to-ports flag requires a
@@ -313,45 +375,19 @@ func (NATRedirectRequiresProtocol) Name() string {
}
// 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 {
+func (NATRedirectRequiresProtocol) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := natTable(ipv6, "-A", "PREROUTING", "-d", localIP(ipv6), "-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 {
+func (NATRedirectRequiresProtocol) LocalAction(ip net.IP, ipv6 bool) 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
-}
-
// NATOutRedirectTCPPort tests that connections are redirected on specified ports.
type NATOutRedirectTCPPort struct{}
@@ -361,15 +397,14 @@ func (NATOutRedirectTCPPort) Name() string {
}
// ContainerAction implements TestCase.ContainerAction.
-func (NATOutRedirectTCPPort) ContainerAction(ip net.IP) error {
- if err := natTable("-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err != nil {
+func (NATOutRedirectTCPPort) ContainerAction(ip net.IP, ipv6 bool) error {
+ if err := natTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err != nil {
return err
}
timeout := 20 * time.Second
- dest := []byte{127, 0, 0, 1}
localAddr := net.TCPAddr{
- IP: dest,
+ IP: net.ParseIP(localIP(ipv6)),
Port: acceptPort,
}
@@ -397,6 +432,213 @@ func (NATOutRedirectTCPPort) ContainerAction(ip net.IP) error {
}
// LocalAction implements TestCase.LocalAction.
-func (NATOutRedirectTCPPort) LocalAction(ip net.IP) error {
+func (NATOutRedirectTCPPort) LocalAction(ip net.IP, ipv6 bool) error {
return nil
}
+
+// NATLoopbackSkipsPrerouting tests that packets sent via loopback aren't
+// affected by PREROUTING rules.
+type NATLoopbackSkipsPrerouting struct{}
+
+// Name implements TestCase.Name.
+func (NATLoopbackSkipsPrerouting) Name() string {
+ return "NATLoopbackSkipsPrerouting"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (NATLoopbackSkipsPrerouting) ContainerAction(ip net.IP, ipv6 bool) error {
+ // Redirect anything sent to localhost to an unused port.
+ dest := []byte{127, 0, 0, 1}
+ if err := natTable(ipv6, "-A", "PREROUTING", "-p", "tcp", "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", dropPort)); err != nil {
+ return err
+ }
+
+ // Establish a connection via localhost. If the PREROUTING rule did apply to
+ // loopback traffic, the connection would fail.
+ sendCh := make(chan error)
+ go func() {
+ sendCh <- connectTCP(dest, acceptPort, sendloopDuration)
+ }()
+
+ if err := listenTCP(acceptPort, sendloopDuration); err != nil {
+ return err
+ }
+ return <-sendCh
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (NATLoopbackSkipsPrerouting) LocalAction(ip net.IP, ipv6 bool) error {
+ // No-op.
+ return nil
+}
+
+// NATPreOriginalDst tests that SO_ORIGINAL_DST returns the pre-NAT destination
+// of PREROUTING NATted packets.
+type NATPreOriginalDst struct{}
+
+// Name implements TestCase.Name.
+func (NATPreOriginalDst) Name() string {
+ return "NATPreOriginalDst"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (NATPreOriginalDst) ContainerAction(ip net.IP, ipv6 bool) error {
+ // Redirect incoming TCP connections to acceptPort.
+ if err := natTable(ipv6, "-A", "PREROUTING",
+ "-p", "tcp",
+ "--destination-port", fmt.Sprintf("%d", dropPort),
+ "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort)); err != nil {
+ return err
+ }
+
+ addrs, err := getInterfaceAddrs(ipv6)
+ if err != nil {
+ return err
+ }
+ return listenForRedirectedConn(ipv6, addrs)
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (NATPreOriginalDst) LocalAction(ip net.IP, ipv6 bool) error {
+ return connectTCP(ip, dropPort, sendloopDuration)
+}
+
+// NATOutOriginalDst tests that SO_ORIGINAL_DST returns the pre-NAT destination
+// of OUTBOUND NATted packets.
+type NATOutOriginalDst struct{}
+
+// Name implements TestCase.Name.
+func (NATOutOriginalDst) Name() string {
+ return "NATOutOriginalDst"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (NATOutOriginalDst) ContainerAction(ip net.IP, ipv6 bool) error {
+ // Redirect incoming TCP connections to acceptPort.
+ if err := natTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort)); err != nil {
+ return err
+ }
+
+ connCh := make(chan error)
+ go func() {
+ connCh <- connectTCP(ip, dropPort, sendloopDuration)
+ }()
+
+ if err := listenForRedirectedConn(ipv6, []net.IP{ip}); err != nil {
+ return err
+ }
+ return <-connCh
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (NATOutOriginalDst) LocalAction(ip net.IP, ipv6 bool) error {
+ // No-op.
+ return nil
+}
+
+func listenForRedirectedConn(ipv6 bool, originalDsts []net.IP) error {
+ // The net package doesn't give guarantee access to the connection's
+ // underlying FD, and thus we cannot call getsockopt. We have to use
+ // traditional syscalls for SO_ORIGINAL_DST.
+
+ // Create the listening socket, bind, listen, and accept.
+ family := syscall.AF_INET
+ if ipv6 {
+ family = syscall.AF_INET6
+ }
+ sockfd, err := syscall.Socket(family, syscall.SOCK_STREAM, 0)
+ if err != nil {
+ return err
+ }
+ defer syscall.Close(sockfd)
+
+ var bindAddr syscall.Sockaddr
+ if ipv6 {
+ bindAddr = &syscall.SockaddrInet6{
+ Port: acceptPort,
+ Addr: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // in6addr_any
+ }
+ } else {
+ bindAddr = &syscall.SockaddrInet4{
+ Port: acceptPort,
+ Addr: [4]byte{0, 0, 0, 0}, // INADDR_ANY
+ }
+ }
+ if err := syscall.Bind(sockfd, bindAddr); err != nil {
+ return err
+ }
+
+ if err := syscall.Listen(sockfd, 1); err != nil {
+ return err
+ }
+
+ connfd, _, err := syscall.Accept(sockfd)
+ if err != nil {
+ return err
+ }
+ defer syscall.Close(connfd)
+
+ // Verify that, despite listening on acceptPort, SO_ORIGINAL_DST
+ // indicates the packet was sent to originalDst:dropPort.
+ if ipv6 {
+ got, err := originalDestination6(connfd)
+ if err != nil {
+ return err
+ }
+ // The original destination could be any of our IPs.
+ for _, dst := range originalDsts {
+ want := syscall.RawSockaddrInet6{
+ Family: syscall.AF_INET6,
+ Port: htons(dropPort),
+ }
+ copy(want.Addr[:], dst.To16())
+ if got == want {
+ return nil
+ }
+ }
+ return fmt.Errorf("SO_ORIGINAL_DST returned %+v, but wanted one of %+v (note: port numbers are in network byte order)", got, originalDsts)
+ } else {
+ got, err := originalDestination4(connfd)
+ if err != nil {
+ return err
+ }
+ // The original destination could be any of our IPs.
+ for _, dst := range originalDsts {
+ want := syscall.RawSockaddrInet4{
+ Family: syscall.AF_INET,
+ Port: htons(dropPort),
+ }
+ copy(want.Addr[:], dst.To4())
+ if got == want {
+ return nil
+ }
+ }
+ return fmt.Errorf("SO_ORIGINAL_DST returned %+v, but wanted one of %+v (note: port numbers are in network byte order)", got, originalDsts)
+ }
+}
+
+// loopbackTests runs an iptables rule and ensures that packets sent to
+// dest:dropPort are received by localhost:acceptPort.
+func loopbackTest(ipv6 bool, dest net.IP, args ...string) error {
+ if err := natTable(ipv6, 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
+}
diff --git a/test/iptables/runner/main.go b/test/iptables/runner/main.go
index 6f77c0684..69d3ef121 100644
--- a/test/iptables/runner/main.go
+++ b/test/iptables/runner/main.go
@@ -24,7 +24,10 @@ import (
"gvisor.dev/gvisor/test/iptables"
)
-var name = flag.String("name", "", "name of the test to run")
+var (
+ name = flag.String("name", "", "name of the test to run")
+ ipv6 = flag.Bool("ipv6", false, "whether the test utilizes ip6tables")
+)
func main() {
flag.Parse()
@@ -43,7 +46,7 @@ func main() {
}
// Run the test.
- if err := test.ContainerAction(ip); err != nil {
+ if err := test.ContainerAction(ip, *ipv6); err != nil {
log.Fatalf("Failed running test %q: %v", *name, err)
}
@@ -57,7 +60,7 @@ func getIP() (net.IP, error) {
localAddr := net.TCPAddr{
Port: iptables.IPExchangePort,
}
- listener, err := net.ListenTCP("tcp4", &localAddr)
+ listener, err := net.ListenTCP("tcp", &localAddr)
if err != nil {
return net.IP{}, fmt.Errorf("failed listening for IP: %v", err)
}