diff options
Diffstat (limited to 'test/iptables')
-rw-r--r-- | test/iptables/BUILD | 36 | ||||
-rw-r--r-- | test/iptables/README.md | 54 | ||||
-rw-r--r-- | test/iptables/filter_input.go | 675 | ||||
-rw-r--r-- | test/iptables/filter_output.go | 145 | ||||
-rw-r--r-- | test/iptables/iptables.go | 53 | ||||
-rw-r--r-- | test/iptables/iptables_test.go | 391 | ||||
-rw-r--r-- | test/iptables/iptables_util.go | 171 | ||||
-rw-r--r-- | test/iptables/nat.go | 331 | ||||
-rw-r--r-- | test/iptables/runner/BUILD | 23 | ||||
-rw-r--r-- | test/iptables/runner/Dockerfile | 4 | ||||
-rw-r--r-- | test/iptables/runner/main.go | 70 |
11 files changed, 0 insertions, 1953 deletions
diff --git a/test/iptables/BUILD b/test/iptables/BUILD deleted file mode 100644 index 6bb3b82b5..000000000 --- a/test/iptables/BUILD +++ /dev/null @@ -1,36 +0,0 @@ -load("//tools:defs.bzl", "go_library", "go_test") - -package(licenses = ["notice"]) - -go_library( - name = "iptables", - testonly = 1, - srcs = [ - "filter_input.go", - "filter_output.go", - "iptables.go", - "iptables_util.go", - "nat.go", - ], - visibility = ["//test/iptables:__subpackages__"], - deps = [ - "//runsc/testutil", - ], -) - -go_test( - name = "iptables_test", - srcs = [ - "iptables_test.go", - ], - library = ":iptables", - tags = [ - "local", - "manual", - ], - deps = [ - "//pkg/log", - "//runsc/dockerutil", - "//runsc/testutil", - ], -) diff --git a/test/iptables/README.md b/test/iptables/README.md deleted file mode 100644 index cc8a2fcac..000000000 --- a/test/iptables/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# iptables Tests - -iptables tests are run via `scripts/iptables_test.sh`. - -iptables requires raw socket support, so you must add the `--net-raw=true` flag -to `/etc/docker/daemon.json` in order to use it. - -## Test Structure - -Each test implements `TestCase`, providing (1) a function to run inside the -container and (2) a function to run locally. Those processes are given each -others' IP addresses. The test succeeds when both functions succeed. - -The function inside the container (`ContainerAction`) typically sets some -iptables rules and then tries to send or receive packets. The local function -(`LocalAction`) will typically just send or receive packets. - -### Adding Tests - -1) Add your test to the `iptables` package. - -2) Register the test in an `init` function via `RegisterTestCase` (see -`filter_input.go` as an example). - -3) Add it to `iptables_test.go` (see the other tests in that file). - -Your test is now runnable with bazel! - -## Run individual tests - -Build and install `runsc`. Re-run this when you modify gVisor: - -```bash -$ bazel build //runsc && sudo cp bazel-bin/runsc/linux_amd64_pure_stripped/runsc $(which runsc) -``` - -Build the testing Docker container. Re-run this when you modify the test code in -this directory: - -```bash -$ bazel run //test/iptables/runner:runner-image -- --norun -``` - -Run an individual test via: - -```bash -$ bazel test //test/iptables:iptables_test --test_filter=<TESTNAME> -``` - -To run an individual test with `runc`: - -```bash -$ bazel test //test/iptables:iptables_test --test_filter=<TESTNAME> --test_arg=--runtime=runc -``` diff --git a/test/iptables/filter_input.go b/test/iptables/filter_input.go deleted file mode 100644 index 4ccd4cce7..000000000 --- a/test/iptables/filter_input.go +++ /dev/null @@ -1,675 +0,0 @@ -// Copyright 2019 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 ( - "errors" - "fmt" - "net" - "time" -) - -const ( - dropPort = 2401 - acceptPort = 2402 - sendloopDuration = 2 * time.Second - network = "udp4" - chainName = "foochain" -) - -func init() { - RegisterTestCase(FilterInputDropAll{}) - RegisterTestCase(FilterInputDropDifferentUDPPort{}) - RegisterTestCase(FilterInputDropOnlyUDP{}) - RegisterTestCase(FilterInputDropTCPDestPort{}) - RegisterTestCase(FilterInputDropTCPSrcPort{}) - RegisterTestCase(FilterInputDropUDPPort{}) - RegisterTestCase(FilterInputDropUDP{}) - RegisterTestCase(FilterInputCreateUserChain{}) - RegisterTestCase(FilterInputDefaultPolicyAccept{}) - RegisterTestCase(FilterInputDefaultPolicyDrop{}) - RegisterTestCase(FilterInputReturnUnderflow{}) - RegisterTestCase(FilterInputSerializeJump{}) - RegisterTestCase(FilterInputJumpBasic{}) - RegisterTestCase(FilterInputJumpReturn{}) - RegisterTestCase(FilterInputJumpReturnDrop{}) - RegisterTestCase(FilterInputJumpBuiltin{}) - RegisterTestCase(FilterInputJumpTwice{}) - RegisterTestCase(FilterInputDestination{}) - RegisterTestCase(FilterInputInvertDestination{}) -} - -// FilterInputDropUDP tests that we can drop UDP traffic. -type FilterInputDropUDP struct{} - -// Name implements TestCase.Name. -func (FilterInputDropUDP) Name() string { - return "FilterInputDropUDP" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputDropUDP) ContainerAction(ip net.IP) error { - if err := filterTable("-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil { - return err - } - - // Listen for UDP packets on dropPort. - if err := listenUDP(dropPort, sendloopDuration); err == nil { - return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort) - } else if netErr, ok := err.(net.Error); !ok || !netErr.Timeout() { - return fmt.Errorf("error reading: %v", err) - } - - // At this point we know that reading timed out and never received a - // packet. - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputDropUDP) LocalAction(ip net.IP) error { - return sendUDPLoop(ip, dropPort, sendloopDuration) -} - -// FilterInputDropOnlyUDP tests that "-p udp -j DROP" only affects UDP traffic. -type FilterInputDropOnlyUDP struct{} - -// Name implements TestCase.Name. -func (FilterInputDropOnlyUDP) Name() string { - return "FilterInputDropOnlyUDP" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputDropOnlyUDP) ContainerAction(ip net.IP) error { - if err := filterTable("-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil { - return err - } - - // Listen for a TCP connection, which should be allowed. - if err := listenTCP(acceptPort, sendloopDuration); err != nil { - return fmt.Errorf("failed to establish a connection %v", err) - } - - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputDropOnlyUDP) LocalAction(ip net.IP) error { - // Try to establish a TCP connection with the container, which should - // succeed. - return connectTCP(ip, acceptPort, sendloopDuration) -} - -// FilterInputDropUDPPort tests that we can drop UDP traffic by port. -type FilterInputDropUDPPort struct{} - -// Name implements TestCase.Name. -func (FilterInputDropUDPPort) Name() string { - return "FilterInputDropUDPPort" -} - -// 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 { - return err - } - - // Listen for UDP packets on dropPort. - if err := listenUDP(dropPort, sendloopDuration); err == nil { - return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort) - } else if netErr, ok := err.(net.Error); !ok || !netErr.Timeout() { - return fmt.Errorf("error reading: %v", err) - } - - // At this point we know that reading timed out and never received a - // packet. - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputDropUDPPort) LocalAction(ip net.IP) error { - return sendUDPLoop(ip, dropPort, sendloopDuration) -} - -// FilterInputDropDifferentUDPPort tests that dropping traffic for a single UDP port -// doesn't drop packets on other ports. -type FilterInputDropDifferentUDPPort struct{} - -// Name implements TestCase.Name. -func (FilterInputDropDifferentUDPPort) Name() string { - return "FilterInputDropDifferentUDPPort" -} - -// 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 { - return err - } - - // Listen for UDP packets on another port. - if err := listenUDP(acceptPort, sendloopDuration); err != nil { - return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %v", acceptPort, err) - } - - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputDropDifferentUDPPort) LocalAction(ip net.IP) error { - return sendUDPLoop(ip, acceptPort, sendloopDuration) -} - -// FilterInputDropTCPDestPort tests that connections are not accepted on specified source ports. -type FilterInputDropTCPDestPort struct{} - -// Name implements TestCase.Name. -func (FilterInputDropTCPDestPort) Name() string { - return "FilterInputDropTCPDestPort" -} - -// 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 { - return err - } - - // Listen for TCP packets on drop port. - if err := listenTCP(dropPort, sendloopDuration); err == nil { - return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort) - } - - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputDropTCPDestPort) LocalAction(ip net.IP) error { - // After the container sets its DROP rule, we shouldn't be able to connect. - // However, we may succeed in connecting if this runs before the container - // sets the rule. To avoid this race, we retry connecting until - // sendloopDuration has elapsed, ignoring whether the connect succeeds. The - // test works becuase the container will error if a connection is - // established after the rule is set. - for start := time.Now(); time.Since(start) < sendloopDuration; { - connectTCP(ip, dropPort, sendloopDuration-time.Since(start)) - } - - return nil -} - -// FilterInputDropTCPSrcPort tests that connections are not accepted on specified source ports. -type FilterInputDropTCPSrcPort struct{} - -// Name implements TestCase.Name. -func (FilterInputDropTCPSrcPort) Name() string { - return "FilterInputDropTCPSrcPort" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputDropTCPSrcPort) ContainerAction(ip net.IP) error { - // Drop anything from an ephemeral port. - if err := filterTable("-A", "INPUT", "-p", "tcp", "-m", "tcp", "--sport", "1024:65535", "-j", "DROP"); err != nil { - return err - } - - // Listen for TCP packets on accept port. - if err := listenTCP(acceptPort, sendloopDuration); err == nil { - return fmt.Errorf("connection destined to port %d should not be accepted, but was", dropPort) - } - - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputDropTCPSrcPort) LocalAction(ip net.IP) error { - // After the container sets its DROP rule, we shouldn't be able to connect. - // However, we may succeed in connecting if this runs before the container - // sets the rule. To avoid this race, we retry connecting until - // sendloopDuration has elapsed, ignoring whether the connect succeeds. The - // test works becuase the container will error if a connection is - // established after the rule is set. - for start := time.Now(); time.Since(start) < sendloopDuration; { - connectTCP(ip, acceptPort, sendloopDuration-time.Since(start)) - } - - return nil -} - -// FilterInputDropAll tests that we can drop all traffic to the INPUT chain. -type FilterInputDropAll struct{} - -// Name implements TestCase.Name. -func (FilterInputDropAll) Name() string { - return "FilterInputDropAll" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputDropAll) ContainerAction(ip net.IP) error { - if err := filterTable("-A", "INPUT", "-j", "DROP"); err != nil { - return err - } - - // Listen for all packets on dropPort. - if err := listenUDP(dropPort, sendloopDuration); err == nil { - return fmt.Errorf("packets should have been dropped, but got a packet") - } else if netErr, ok := err.(net.Error); !ok || !netErr.Timeout() { - return fmt.Errorf("error reading: %v", err) - } - - // At this point we know that reading timed out and never received a - // packet. - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputDropAll) LocalAction(ip net.IP) error { - return sendUDPLoop(ip, dropPort, sendloopDuration) -} - -// FilterInputMultiUDPRules verifies that multiple UDP rules are applied -// correctly. This has the added benefit of testing whether we're serializing -// rules correctly -- if we do it incorrectly, the iptables tool will -// misunderstand and save the wrong tables. -type FilterInputMultiUDPRules struct{} - -// Name implements TestCase.Name. -func (FilterInputMultiUDPRules) Name() string { - return "FilterInputMultiUDPRules" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputMultiUDPRules) ContainerAction(ip net.IP) 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) -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputMultiUDPRules) LocalAction(ip net.IP) error { - // No-op. - return nil -} - -// FilterInputRequireProtocolUDP checks that "-m udp" requires "-p udp" to be -// specified. -type FilterInputRequireProtocolUDP struct{} - -// Name implements TestCase.Name. -func (FilterInputRequireProtocolUDP) Name() string { - return "FilterInputRequireProtocolUDP" -} - -// 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 { - return errors.New("expected iptables to fail with out \"-p udp\", but succeeded") - } - return nil -} - -func (FilterInputRequireProtocolUDP) LocalAction(ip net.IP) error { - // No-op. - return nil -} - -// FilterInputCreateUserChain tests chain creation. -type FilterInputCreateUserChain struct{} - -// Name implements TestCase.Name. -func (FilterInputCreateUserChain) Name() string { - return "FilterInputCreateUserChain" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputCreateUserChain) ContainerAction(ip net.IP) error { - rules := [][]string{ - // Create a chain. - {"-N", chainName}, - // Add a simple rule to the chain. - {"-A", chainName, "-j", "DROP"}, - } - return filterTableRules(rules) -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputCreateUserChain) LocalAction(ip net.IP) error { - // No-op. - return nil -} - -// FilterInputDefaultPolicyAccept tests the default ACCEPT policy. -type FilterInputDefaultPolicyAccept struct{} - -// Name implements TestCase.Name. -func (FilterInputDefaultPolicyAccept) Name() string { - return "FilterInputDefaultPolicyAccept" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputDefaultPolicyAccept) ContainerAction(ip net.IP) error { - // Set the default policy to accept, then receive a packet. - if err := filterTable("-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) -} - -// FilterInputDefaultPolicyDrop tests the default DROP policy. -type FilterInputDefaultPolicyDrop struct{} - -// Name implements TestCase.Name. -func (FilterInputDefaultPolicyDrop) Name() string { - return "FilterInputDefaultPolicyDrop" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputDefaultPolicyDrop) ContainerAction(ip net.IP) error { - if err := filterTable("-P", "INPUT", "DROP"); err != nil { - return err - } - - // Listen for UDP packets on dropPort. - if err := listenUDP(dropPort, sendloopDuration); err == nil { - return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort) - } else if netErr, ok := err.(net.Error); !ok || !netErr.Timeout() { - return fmt.Errorf("error reading: %v", err) - } - - // At this point we know that reading timed out and never received a - // packet. - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputDefaultPolicyDrop) LocalAction(ip net.IP) error { - return sendUDPLoop(ip, acceptPort, sendloopDuration) -} - -// FilterInputReturnUnderflow tests that -j RETURN in a built-in chain causes -// the underflow rule (i.e. default policy) to be executed. -type FilterInputReturnUnderflow struct{} - -// Name implements TestCase.Name. -func (FilterInputReturnUnderflow) Name() string { - return "FilterInputReturnUnderflow" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputReturnUnderflow) ContainerAction(ip net.IP) error { - // Add a RETURN rule followed by an unconditional accept, and set the - // default policy to DROP. - rules := [][]string{ - {"-A", "INPUT", "-j", "RETURN"}, - {"-A", "INPUT", "-j", "DROP"}, - {"-P", "INPUT", "ACCEPT"}, - } - if err := filterTableRules(rules); err != nil { - return err - } - - // We should receive packets, as the RETURN rule will trigger the default - // ACCEPT policy. - return listenUDP(acceptPort, sendloopDuration) -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputReturnUnderflow) LocalAction(ip net.IP) error { - return sendUDPLoop(ip, acceptPort, sendloopDuration) -} - -// FilterInputSerializeJump verifies that we can serialize jumps. -type FilterInputSerializeJump struct{} - -// Name implements TestCase.Name. -func (FilterInputSerializeJump) Name() string { - return "FilterInputSerializeJump" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputSerializeJump) ContainerAction(ip net.IP) error { - // Write a JUMP rule, the serialize it with `-L`. - rules := [][]string{ - {"-N", chainName}, - {"-A", "INPUT", "-j", chainName}, - {"-L"}, - } - return filterTableRules(rules) -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputSerializeJump) LocalAction(ip net.IP) error { - // No-op. - return nil -} - -// FilterInputJumpBasic jumps to a chain and executes a rule there. -type FilterInputJumpBasic struct{} - -// Name implements TestCase.Name. -func (FilterInputJumpBasic) Name() string { - return "FilterInputJumpBasic" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputJumpBasic) ContainerAction(ip net.IP) error { - rules := [][]string{ - {"-P", "INPUT", "DROP"}, - {"-N", chainName}, - {"-A", "INPUT", "-j", chainName}, - {"-A", chainName, "-j", "ACCEPT"}, - } - if err := filterTableRules(rules); err != nil { - return err - } - - // Listen for UDP packets on acceptPort. - return listenUDP(acceptPort, sendloopDuration) -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputJumpBasic) LocalAction(ip net.IP) error { - return sendUDPLoop(ip, acceptPort, sendloopDuration) -} - -// FilterInputJumpReturn jumps, returns, and executes a rule. -type FilterInputJumpReturn struct{} - -// Name implements TestCase.Name. -func (FilterInputJumpReturn) Name() string { - return "FilterInputJumpReturn" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputJumpReturn) ContainerAction(ip net.IP) error { - rules := [][]string{ - {"-N", chainName}, - {"-P", "INPUT", "ACCEPT"}, - {"-A", "INPUT", "-j", chainName}, - {"-A", chainName, "-j", "RETURN"}, - {"-A", chainName, "-j", "DROP"}, - } - if err := filterTableRules(rules); err != nil { - return err - } - - // Listen for UDP packets on acceptPort. - return listenUDP(acceptPort, sendloopDuration) -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputJumpReturn) LocalAction(ip net.IP) error { - return sendUDPLoop(ip, acceptPort, sendloopDuration) -} - -// FilterInputJumpReturnDrop jumps to a chain, returns, and DROPs packets. -type FilterInputJumpReturnDrop struct{} - -// Name implements TestCase.Name. -func (FilterInputJumpReturnDrop) Name() string { - return "FilterInputJumpReturnDrop" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputJumpReturnDrop) ContainerAction(ip net.IP) error { - rules := [][]string{ - {"-N", chainName}, - {"-A", "INPUT", "-j", chainName}, - {"-A", "INPUT", "-j", "DROP"}, - {"-A", chainName, "-j", "RETURN"}, - } - if err := filterTableRules(rules); err != nil { - return err - } - - // Listen for UDP packets on dropPort. - if err := listenUDP(dropPort, sendloopDuration); err == nil { - return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort) - } else if netErr, ok := err.(net.Error); !ok || !netErr.Timeout() { - return fmt.Errorf("error reading: %v", err) - } - - // At this point we know that reading timed out and never received a - // packet. - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (FilterInputJumpReturnDrop) LocalAction(ip net.IP) error { - return sendUDPLoop(ip, dropPort, sendloopDuration) -} - -// FilterInputJumpBuiltin verifies that jumping to a top-levl chain is illegal. -type FilterInputJumpBuiltin struct{} - -// Name implements TestCase.Name. -func (FilterInputJumpBuiltin) Name() string { - return "FilterInputJumpBuiltin" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputJumpBuiltin) ContainerAction(ip net.IP) error { - if err := filterTable("-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 { - // No-op. - return nil -} - -// FilterInputJumpTwice jumps twice, then returns twice and executes a rule. -type FilterInputJumpTwice struct{} - -// Name implements TestCase.Name. -func (FilterInputJumpTwice) Name() string { - return "FilterInputJumpTwice" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterInputJumpTwice) ContainerAction(ip net.IP) error { - const chainName2 = chainName + "2" - rules := [][]string{ - {"-P", "INPUT", "DROP"}, - {"-N", chainName}, - {"-N", chainName2}, - {"-A", "INPUT", "-j", chainName}, - {"-A", chainName, "-j", chainName2}, - {"-A", "INPUT", "-j", "ACCEPT"}, - } - if err := filterTableRules(rules); err != nil { - return err - } - - // UDP packets should jump and return twice, eventually hitting the - // ACCEPT rule. - return listenUDP(acceptPort, sendloopDuration) -} - -// LocalAction implements TestCase.LocalAction. -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) -} diff --git a/test/iptables/filter_output.go b/test/iptables/filter_output.go deleted file mode 100644 index 4582d514c..000000000 --- a/test/iptables/filter_output.go +++ /dev/null @@ -1,145 +0,0 @@ -// 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" - "net" -) - -func init() { - RegisterTestCase(FilterOutputDropTCPDestPort{}) - RegisterTestCase(FilterOutputDropTCPSrcPort{}) - RegisterTestCase(FilterOutputDestination{}) - RegisterTestCase(FilterOutputInvertDestination{}) -} - -// FilterOutputDropTCPDestPort tests that connections are not accepted on -// specified source ports. -type FilterOutputDropTCPDestPort struct{} - -// Name implements TestCase.Name. -func (FilterOutputDropTCPDestPort) Name() string { - return "FilterOutputDropTCPDestPort" -} - -// ContainerAction implements TestCase.ContainerAction. -func (FilterOutputDropTCPDestPort) ContainerAction(ip net.IP) error { - if err := filterTable("-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil { - return err - } - - // Listen for TCP packets on accept port. - if err := listenTCP(acceptPort, sendloopDuration); err == nil { - return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", dropPort) - } - - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (FilterOutputDropTCPDestPort) LocalAction(ip net.IP) error { - if err := connectTCP(ip, acceptPort, sendloopDuration); err == nil { - return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort) - } - - return nil -} - -// FilterOutputDropTCPSrcPort tests that connections are not accepted on -// specified source ports. -type FilterOutputDropTCPSrcPort struct{} - -// Name implements TestCase.Name. -func (FilterOutputDropTCPSrcPort) Name() string { - return "FilterOutputDropTCPSrcPort" -} - -// 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 { - return err - } - - // Listen for TCP packets on drop port. - if err := listenTCP(dropPort, sendloopDuration); err == nil { - return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort) - } - - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (FilterOutputDropTCPSrcPort) LocalAction(ip net.IP) 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) - } - - 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) -} diff --git a/test/iptables/iptables.go b/test/iptables/iptables.go deleted file mode 100644 index 2e565d988..000000000 --- a/test/iptables/iptables.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 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 contains a set of iptables tests implemented as TestCases -package iptables - -import ( - "fmt" - "net" -) - -// IPExchangePort is the port the container listens on to receive the IP -// address of the local process. -const IPExchangePort = 2349 - -// A TestCase contains one action to run in the container and one to run -// locally. The actions run concurrently and each must succeed for the test -// pass. -type TestCase interface { - // Name returns the name of the test. - Name() string - - // ContainerAction runs inside the container. It receives the IP of the - // local process. - ContainerAction(ip net.IP) error - - // LocalAction runs locally. It receives the IP of the container. - LocalAction(ip net.IP) error -} - -// Tests maps test names to TestCase. -// -// New TestCases are added by calling RegisterTestCase in an init function. -var Tests = map[string]TestCase{} - -// RegisterTestCase registers tc so it can be run. -func RegisterTestCase(tc TestCase) { - if _, ok := Tests[tc.Name()]; ok { - panic(fmt.Sprintf("TestCase %s already registered.", tc.Name())) - } - Tests[tc.Name()] = tc -} diff --git a/test/iptables/iptables_test.go b/test/iptables/iptables_test.go deleted file mode 100644 index 7f1f70606..000000000 --- a/test/iptables/iptables_test.go +++ /dev/null @@ -1,391 +0,0 @@ -// Copyright 2019 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 ( - "flag" - "fmt" - "net" - "os" - "path" - "testing" - "time" - - "gvisor.dev/gvisor/pkg/log" - "gvisor.dev/gvisor/runsc/dockerutil" - "gvisor.dev/gvisor/runsc/testutil" -) - -const timeout = 18 * time.Second - -var image = flag.String("image", "bazel/test/iptables/runner:runner-image", "image to run tests in") - -type result struct { - output string - err error -} - -// singleTest runs a TestCase. Each test follows a pattern: -// - Create a container. -// - Get the container's IP. -// - Send the container our IP. -// - Start a new goroutine running the local action of the test. -// - Wait for both the container and local actions to finish. -// -// Container output is logged to $TEST_UNDECLARED_OUTPUTS_DIR if it exists, or -// to stderr. -func singleTest(test TestCase) error { - if _, ok := Tests[test.Name()]; !ok { - return fmt.Errorf("no test found with name %q. Has it been registered?", test.Name()) - } - - // Create and start the container. - cont := dockerutil.MakeDocker("gvisor-iptables") - defer cont.CleanUp() - resultChan := make(chan *result) - go func() { - output, err := cont.RunFg("--cap-add=NET_ADMIN", *image, "-name", test.Name()) - logContainer(output, err) - resultChan <- &result{output, err} - }() - - // Get the container IP. - ip, err := getIP(cont) - if err != nil { - return fmt.Errorf("failed to get container IP: %v", err) - } - - // Give the container our IP. - if err := sendIP(ip); err != nil { - return fmt.Errorf("failed to send IP to container: %v", err) - } - - // Run our side of the test. - errChan := make(chan error) - go func() { - errChan <- test.LocalAction(ip) - }() - - // Wait for both the container and local tests to finish. - var res *result - to := time.After(timeout) - for localDone := false; res == nil || !localDone; { - select { - case res = <-resultChan: - log.Infof("Container finished.") - case err, localDone = <-errChan: - log.Infof("Local finished.") - if err != nil { - return fmt.Errorf("local test failed: %v", err) - } - case <-to: - return fmt.Errorf("timed out after %f seconds", timeout.Seconds()) - } - } - - return res.err -} - -func getIP(cont dockerutil.Docker) (net.IP, error) { - // The container might not have started yet, so retry a few times. - var ipStr string - to := time.After(timeout) - for ipStr == "" { - ipStr, _ = cont.FindIP() - select { - case <-to: - return net.IP{}, fmt.Errorf("timed out getting IP after %f seconds", timeout.Seconds()) - default: - time.Sleep(250 * time.Millisecond) - } - } - ip := net.ParseIP(ipStr) - if ip == nil { - return net.IP{}, fmt.Errorf("invalid IP: %q", ipStr) - } - log.Infof("Container has IP of %s", ipStr) - return ip, nil -} - -func sendIP(ip net.IP) error { - contAddr := net.TCPAddr{ - IP: ip, - Port: IPExchangePort, - } - var conn *net.TCPConn - // The container may not be listening when we first connect, so retry - // upon error. - cb := func() error { - c, err := net.DialTCP("tcp4", nil, &contAddr) - conn = c - return err - } - if err := testutil.Poll(cb, timeout); err != nil { - return fmt.Errorf("timed out waiting to send IP, most recent error: %v", err) - } - if _, err := conn.Write([]byte{0}); err != nil { - return fmt.Errorf("error writing to container: %v", err) - } - return nil -} - -func logContainer(output string, err error) { - msg := fmt.Sprintf("Container error: %v\nContainer output:\n%v", err, output) - if artifactsDir := os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR"); artifactsDir != "" { - fpath := path.Join(artifactsDir, "container.log") - if file, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE, 0644); err != nil { - log.Warningf("Failed to open log file %q: %v", fpath, err) - } else { - defer file.Close() - if _, err := file.Write([]byte(msg)); err == nil { - return - } - log.Warningf("Failed to write to log file %s: %v", fpath, err) - } - } - - // We couldn't write to the output directory -- just log to stderr. - log.Infof(msg) -} - -func TestFilterInputDropUDP(t *testing.T) { - if err := singleTest(FilterInputDropUDP{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterInputDropUDPPort(t *testing.T) { - if err := singleTest(FilterInputDropUDPPort{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterInputDropDifferentUDPPort(t *testing.T) { - if err := singleTest(FilterInputDropDifferentUDPPort{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterInputDropAll(t *testing.T) { - if err := singleTest(FilterInputDropAll{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterInputDropOnlyUDP(t *testing.T) { - if err := singleTest(FilterInputDropOnlyUDP{}); err != nil { - t.Fatal(err) - } -} - -func TestNATRedirectUDPPort(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(NATRedirectUDPPort{}); err != nil { - t.Fatal(err) - } -} - -func TestNATRedirectTCPPort(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(NATRedirectTCPPort{}); err != nil { - t.Fatal(err) - } -} - -func TestNATDropUDP(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(NATDropUDP{}); err != nil { - t.Fatal(err) - } -} - -func TestNATAcceptAll(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(NATAcceptAll{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterInputDropTCPDestPort(t *testing.T) { - if err := singleTest(FilterInputDropTCPDestPort{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterInputDropTCPSrcPort(t *testing.T) { - if err := singleTest(FilterInputDropTCPSrcPort{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterInputCreateUserChain(t *testing.T) { - if err := singleTest(FilterInputCreateUserChain{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterInputDefaultPolicyAccept(t *testing.T) { - if err := singleTest(FilterInputDefaultPolicyAccept{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterInputDefaultPolicyDrop(t *testing.T) { - if err := singleTest(FilterInputDefaultPolicyDrop{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterInputReturnUnderflow(t *testing.T) { - if err := singleTest(FilterInputReturnUnderflow{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterOutputDropTCPDestPort(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("filter OUTPUT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(FilterOutputDropTCPDestPort{}); err != nil { - t.Fatal(err) - } -} - -func TestFilterOutputDropTCPSrcPort(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("filter OUTPUT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(FilterOutputDropTCPSrcPort{}); err != nil { - t.Fatal(err) - } -} - -func TestJumpSerialize(t *testing.T) { - if err := singleTest(FilterInputSerializeJump{}); err != nil { - t.Fatal(err) - } -} - -func TestJumpBasic(t *testing.T) { - if err := singleTest(FilterInputJumpBasic{}); err != nil { - t.Fatal(err) - } -} - -func TestJumpReturn(t *testing.T) { - if err := singleTest(FilterInputJumpReturn{}); err != nil { - t.Fatal(err) - } -} - -func TestJumpReturnDrop(t *testing.T) { - if err := singleTest(FilterInputJumpReturnDrop{}); err != nil { - t.Fatal(err) - } -} - -func TestJumpBuiltin(t *testing.T) { - if err := singleTest(FilterInputJumpBuiltin{}); err != nil { - t.Fatal(err) - } -} - -func TestJumpTwice(t *testing.T) { - if err := singleTest(FilterInputJumpTwice{}); err != nil { - 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) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(NATOutRedirectIP{}); err != nil { - t.Fatal(err) - } -} - -func TestNATOutDontRedirectIP(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(NATOutDontRedirectIP{}); err != nil { - t.Fatal(err) - } -} - -func TestNATOutRedirectInvert(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(NATOutRedirectInvert{}); err != nil { - t.Fatal(err) - } -} - -func TestNATPreRedirectIP(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(NATPreRedirectIP{}); err != nil { - t.Fatal(err) - } -} - -func TestNATPreDontRedirectIP(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(NATPreDontRedirectIP{}); err != nil { - t.Fatal(err) - } -} - -func TestNATPreRedirectInvert(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(NATPreRedirectInvert{}); err != nil { - t.Fatal(err) - } -} - -func TestNATRedirectRequiresProtocol(t *testing.T) { - // TODO(gvisor.dev/issue/170): Enable when supported. - t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).") - if err := singleTest(NATRedirectRequiresProtocol{}); err != nil { - t.Fatal(err) - } -} diff --git a/test/iptables/iptables_util.go b/test/iptables/iptables_util.go deleted file mode 100644 index 134391e8d..000000000 --- a/test/iptables/iptables_util.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2019 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" - "net" - "os/exec" - "time" - - "gvisor.dev/gvisor/runsc/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) -} - -// natTable calls `iptables -t nat` with the given args. -func natTable(args ...string) error { - return tableCmd("nat", args) -} - -func tableCmd(table string, args []string) error { - args = append([]string{"-t", table}, args...) - cmd := exec.Command(iptablesBinary, 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)) - } - return nil -} - -// 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 := tableCmd(table, args); err != nil { - return err - } - } - return nil -} - -// listenUDP listens on a UDP port and returns the value of net.Conn.Read() for -// the first read on that port. -func listenUDP(port int, timeout time.Duration) error { - localAddr := net.UDPAddr{ - Port: port, - } - conn, err := net.ListenUDP(network, &localAddr) - if err != nil { - return err - } - defer conn.Close() - conn.SetDeadline(time.Now().Add(timeout)) - _, err = conn.Read([]byte{0}) - return err -} - -// 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. - remote := net.UDPAddr{ - IP: ip, - Port: port, - } - conn, err := net.DialUDP(network, nil, &remote) - if err != nil { - return err - } - defer conn.Close() - - to := time.After(duration) - for timedOut := false; !timedOut; { - // This may return an error (connection refused) if the remote - // hasn't started listening yet or they're dropping our - // packets. So we ignore Write errors and depend on the remote - // to report a failure if it doesn't get a packet it needs. - conn.Write([]byte{0}) - select { - case <-to: - timedOut = true - default: - time.Sleep(200 * time.Millisecond) - } - } - - return nil -} - -// listenTCP listens for connections on a TCP port. -func listenTCP(port int, timeout time.Duration) error { - localAddr := net.TCPAddr{ - Port: port, - } - - // Starts listening on port. - lConn, err := net.ListenTCP("tcp4", &localAddr) - if err != nil { - return err - } - defer lConn.Close() - - // Accept connections on port. - lConn.SetDeadline(time.Now().Add(timeout)) - conn, err := lConn.AcceptTCP() - if err != nil { - return err - } - conn.Close() - return nil -} - -// connectTCP connects to the given IP and port from an ephemeral local address. -func connectTCP(ip net.IP, port int, timeout time.Duration) error { - contAddr := net.TCPAddr{ - IP: ip, - Port: port, - } - // The container may not be listening when we first connect, so retry - // upon error. - callback := func() error { - conn, err := net.DialTimeout("tcp", contAddr.String(), timeout) - if conn != nil { - conn.Close() - } - return err - } - if err := testutil.Poll(callback, timeout); err != nil { - return fmt.Errorf("timed out waiting to connect IP, most recent error: %v", err) - } - - 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 -} diff --git a/test/iptables/nat.go b/test/iptables/nat.go deleted file mode 100644 index 40096901c..000000000 --- a/test/iptables/nat.go +++ /dev/null @@ -1,331 +0,0 @@ -// 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 ( - "errors" - "fmt" - "net" - "time" -) - -const ( - redirectPort = 42 -) - -func init() { - RegisterTestCase(NATRedirectUDPPort{}) - 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. -type NATRedirectUDPPort struct{} - -// Name implements TestCase.Name. -func (NATRedirectUDPPort) Name() string { - return "NATRedirectUDPPort" -} - -// ContainerAction implements TestCase.ContainerAction. -func (NATRedirectUDPPort) ContainerAction(ip net.IP) error { - if err := natTable("-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil { - return err - } - - if err := listenUDP(redirectPort, sendloopDuration); err != nil { - return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %v", redirectPort, err) - } - - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (NATRedirectUDPPort) LocalAction(ip net.IP) error { - return sendUDPLoop(ip, acceptPort, sendloopDuration) -} - -// NATRedirectTCPPort tests that connections are redirected on specified ports. -type NATRedirectTCPPort struct{} - -// Name implements TestCase.Name. -func (NATRedirectTCPPort) Name() string { - return "NATRedirectTCPPort" -} - -// ContainerAction implements TestCase.ContainerAction. -func (NATRedirectTCPPort) 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", redirectPort)); err != nil { - return err - } - - // Listen for TCP packets on redirect port. - return listenTCP(redirectPort, sendloopDuration) -} - -// LocalAction implements TestCase.LocalAction. -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. -type NATDropUDP struct{} - -// Name implements TestCase.Name. -func (NATDropUDP) Name() string { - return "NATDropUDP" -} - -// 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 { - return err - } - - if err := listenUDP(acceptPort, sendloopDuration); err == nil { - return fmt.Errorf("packets on port %d should have been redirected to port %d", acceptPort, redirectPort) - } - - return nil -} - -// LocalAction implements TestCase.LocalAction. -func (NATDropUDP) LocalAction(ip net.IP) error { - return sendUDPLoop(ip, acceptPort, sendloopDuration) -} - -// NATAcceptAll tests that all UDP packets are accepted. -type NATAcceptAll struct{} - -// Name implements TestCase.Name. -func (NATAcceptAll) Name() string { - return "NATAcceptAll" -} - -// ContainerAction implements TestCase.ContainerAction. -func (NATAcceptAll) ContainerAction(ip net.IP) error { - if err := natTable("-A", "PREROUTING", "-p", "udp", "-j", "ACCEPT"); err != nil { - return err - } - - if err := listenUDP(acceptPort, sendloopDuration); err != nil { - return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %v", acceptPort, err) - } - - return nil -} - -// LocalAction implements TestCase.LocalAction. -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 -} diff --git a/test/iptables/runner/BUILD b/test/iptables/runner/BUILD deleted file mode 100644 index b9199387a..000000000 --- a/test/iptables/runner/BUILD +++ /dev/null @@ -1,23 +0,0 @@ -load("//tools:defs.bzl", "container_image", "go_binary", "go_image") - -package(licenses = ["notice"]) - -go_binary( - name = "runner", - testonly = 1, - srcs = ["main.go"], - deps = ["//test/iptables"], -) - -container_image( - name = "iptables-base", - base = "@iptables-test//image", -) - -go_image( - name = "runner-image", - testonly = 1, - srcs = ["main.go"], - base = ":iptables-base", - deps = ["//test/iptables"], -) diff --git a/test/iptables/runner/Dockerfile b/test/iptables/runner/Dockerfile deleted file mode 100644 index b77db44a1..000000000 --- a/test/iptables/runner/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -# This Dockerfile builds the image hosted at -# gcr.io/gvisor-presubmit/iptables-test. -FROM ubuntu -RUN apt update && apt install -y iptables diff --git a/test/iptables/runner/main.go b/test/iptables/runner/main.go deleted file mode 100644 index 3c794114e..000000000 --- a/test/iptables/runner/main.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019 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 main runs iptables tests from within a docker container. -package main - -import ( - "flag" - "fmt" - "log" - "net" - - "gvisor.dev/gvisor/test/iptables" -) - -var name = flag.String("name", "", "name of the test to run") - -func main() { - flag.Parse() - - // Find out which test we're running. - test, ok := iptables.Tests[*name] - if !ok { - log.Fatalf("No test found named %q", *name) - } - log.Printf("Running test %q", *name) - - // Get the IP of the local process. - ip, err := getIP() - if err != nil { - log.Fatal(err) - } - - // Run the test. - if err := test.ContainerAction(ip); err != nil { - log.Fatalf("Failed running test %q: %v", *name, err) - } -} - -// getIP listens for a connection from the local process and returns the source -// IP of that connection. -func getIP() (net.IP, error) { - localAddr := net.TCPAddr{ - Port: iptables.IPExchangePort, - } - listener, err := net.ListenTCP("tcp4", &localAddr) - if err != nil { - return net.IP{}, fmt.Errorf("failed listening for IP: %v", err) - } - defer listener.Close() - conn, err := listener.AcceptTCP() - if err != nil { - return net.IP{}, fmt.Errorf("failed accepting IP: %v", err) - } - defer conn.Close() - log.Printf("Connected to %v", conn.RemoteAddr()) - - return conn.RemoteAddr().(*net.TCPAddr).IP, nil -} |