summaryrefslogtreecommitdiffhomepage
path: root/test/iptables
diff options
context:
space:
mode:
Diffstat (limited to 'test/iptables')
-rw-r--r--test/iptables/BUILD41
-rw-r--r--test/iptables/README.md84
-rw-r--r--test/iptables/filter_input.go990
-rw-r--r--test/iptables/filter_output.go714
-rw-r--r--test/iptables/iptables.go115
-rw-r--r--test/iptables/iptables_test.go458
-rw-r--r--test/iptables/iptables_unsafe.go64
-rw-r--r--test/iptables/iptables_util.go298
-rw-r--r--test/iptables/nat.go917
-rw-r--r--test/iptables/runner/BUILD12
-rw-r--r--test/iptables/runner/main.go79
11 files changed, 0 insertions, 3772 deletions
diff --git a/test/iptables/BUILD b/test/iptables/BUILD
deleted file mode 100644
index 94d4ca2d4..000000000
--- a/test/iptables/BUILD
+++ /dev/null
@@ -1,41 +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_unsafe.go",
- "iptables_util.go",
- "nat.go",
- ],
- visibility = ["//test/iptables:__subpackages__"],
- deps = [
- "//pkg/binary",
- "//pkg/test/testutil",
- "//pkg/usermem",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-go_test(
- name = "iptables_test",
- size = "large",
- srcs = [
- "iptables_test.go",
- ],
- data = ["//test/iptables/runner"],
- library = ":iptables",
- tags = [
- "local",
- "manual",
- ],
- deps = [
- "//pkg/test/dockerutil",
- "//pkg/test/testutil",
- ],
-)
diff --git a/test/iptables/README.md b/test/iptables/README.md
deleted file mode 100644
index 1196f8eb5..000000000
--- a/test/iptables/README.md
+++ /dev/null
@@ -1,84 +0,0 @@
-# iptables Tests
-
-iptables tests are run via `make iptables-tests`.
-
-iptables require some extra Docker configuration to work. Enable IPv6 in
-`/etc/docker/daemon.json` (make sure to restart Docker if you change this file):
-
-```json
-{
- "experimental": true,
- "fixed-cidr-v6": "2001:db8:1::/64",
- "ipv6": true,
- // Runtimes and other Docker config...
-}
-```
-
-And if you're running manually (i.e. not using the `make` target), you'll need
-to:
-
-* Enable iptables via `modprobe iptables_filter && modprobe ip6table_filter`.
-* Enable `--net-raw` in your chosen runtime in `/etc/docker/daemon.json` (make
- sure to restart Docker if you change this file).
-
-The resulting runtime should look something like this:
-
-```json
-"runsc": {
- "path": "/tmp/iptables/runsc",
- "runtimeArgs": [
- "--debug-log",
- "/tmp/iptables/logs/runsc.log.%TEST%.%TIMESTAMP%.%COMMAND%",
- "--net-raw"
- ]
-},
-// ...
-```
-
-## 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
-$ make load-iptables
-```
-
-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 4739bc06f..000000000
--- a/test/iptables/filter_input.go
+++ /dev/null
@@ -1,990 +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 (
- "context"
- "errors"
- "fmt"
- "net"
- "time"
-)
-
-const (
- dropPort = 2401
- acceptPort = 2402
- sendloopDuration = 2 * time.Second
- 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{})
- RegisterTestCase(&FilterInputSource{})
- RegisterTestCase(&FilterInputInvertSource{})
- RegisterTestCase(&FilterInputInterfaceAccept{})
- RegisterTestCase(&FilterInputInterfaceDrop{})
- RegisterTestCase(&FilterInputInterface{})
- RegisterTestCase(&FilterInputInterfaceBeginsWith{})
- RegisterTestCase(&FilterInputInterfaceInvertDrop{})
- RegisterTestCase(&FilterInputInterfaceInvertAccept{})
-}
-
-// FilterInputDropUDP tests that we can drop UDP traffic.
-type FilterInputDropUDP struct{ containerCase }
-
-var _ TestCase = (*FilterInputDropUDP)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputDropUDP) Name() string {
- return "FilterInputDropUDP"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputDropUDP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil {
- return err
- }
-
- // Listen for UDP packets on dropPort.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenUDP(timedCtx, dropPort, ipv6); err == nil {
- return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- 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(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, dropPort, ipv6)
-}
-
-// FilterInputDropOnlyUDP tests that "-p udp -j DROP" only affects UDP traffic.
-type FilterInputDropOnlyUDP struct{ baseCase }
-
-var _ TestCase = (*FilterInputDropOnlyUDP)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputDropOnlyUDP) Name() string {
- return "FilterInputDropOnlyUDP"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputDropOnlyUDP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil {
- return err
- }
-
- // Listen for a TCP connection, which should be allowed.
- if err := listenTCP(ctx, acceptPort, ipv6); err != nil {
- return fmt.Errorf("failed to establish a connection %v", err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputDropOnlyUDP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // Try to establish a TCP connection with the container, which should
- // succeed.
- return connectTCP(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputDropUDPPort tests that we can drop UDP traffic by port.
-type FilterInputDropUDPPort struct{ containerCase }
-
-var _ TestCase = (*FilterInputDropUDPPort)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputDropUDPPort) Name() string {
- return "FilterInputDropUDPPort"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputDropUDPPort) ContainerAction(ctx context.Context, 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
- }
-
- // Listen for UDP packets on dropPort.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenUDP(timedCtx, dropPort, ipv6); err == nil {
- return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- 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(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, dropPort, ipv6)
-}
-
-// FilterInputDropDifferentUDPPort tests that dropping traffic for a single UDP port
-// doesn't drop packets on other ports.
-type FilterInputDropDifferentUDPPort struct{ containerCase }
-
-var _ TestCase = (*FilterInputDropDifferentUDPPort)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputDropDifferentUDPPort) Name() string {
- return "FilterInputDropDifferentUDPPort"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputDropDifferentUDPPort) ContainerAction(ctx context.Context, 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
- }
-
- // Listen for UDP packets on another port.
- if err := listenUDP(ctx, acceptPort, ipv6); 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(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputDropTCPDestPort tests that connections are not accepted on specified source ports.
-type FilterInputDropTCPDestPort struct{ baseCase }
-
-var _ TestCase = (*FilterInputDropTCPDestPort)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputDropTCPDestPort) Name() string {
- return "FilterInputDropTCPDestPort"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputDropTCPDestPort) ContainerAction(ctx context.Context, 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
- }
-
- // Listen for TCP packets on drop port.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenTCP(timedCtx, dropPort, ipv6); err == nil {
- return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputDropTCPDestPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // Ensure we cannot connect to the container.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := connectTCP(timedCtx, ip, dropPort, ipv6); err == nil {
- return fmt.Errorf("expected not to connect, but was able to connect on port %d", dropPort)
- }
- return nil
-}
-
-// FilterInputDropTCPSrcPort tests that connections are not accepted on specified source ports.
-type FilterInputDropTCPSrcPort struct{ baseCase }
-
-var _ TestCase = (*FilterInputDropTCPSrcPort)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputDropTCPSrcPort) Name() string {
- return "FilterInputDropTCPSrcPort"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputDropTCPSrcPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // Drop anything from an ephemeral port.
- if err := filterTable(ipv6, "-A", "INPUT", "-p", "tcp", "-m", "tcp", "--sport", "1024:65535", "-j", "DROP"); err != nil {
- return err
- }
-
- // Listen for TCP packets on accept port.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection destined to port %d should not be accepted, but was", dropPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputDropTCPSrcPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // Ensure we cannot connect to the container.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := connectTCP(timedCtx, ip, dropPort, ipv6); err == nil {
- return fmt.Errorf("expected not to connect, but was able to connect on port %d", acceptPort)
- }
- return nil
-}
-
-// FilterInputDropAll tests that we can drop all traffic to the INPUT chain.
-type FilterInputDropAll struct{ containerCase }
-
-var _ TestCase = (*FilterInputDropAll)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputDropAll) Name() string {
- return "FilterInputDropAll"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputDropAll) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-A", "INPUT", "-j", "DROP"); err != nil {
- return err
- }
-
- // Listen for all packets on dropPort.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenUDP(timedCtx, dropPort, ipv6); err == nil {
- return fmt.Errorf("packets should have been dropped, but got a packet")
- } else if !errors.Is(err, context.DeadlineExceeded) {
- 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(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, dropPort, ipv6)
-}
-
-// 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{ baseCase }
-
-var _ TestCase = (*FilterInputMultiUDPRules)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputMultiUDPRules) Name() string {
- return "FilterInputMultiUDPRules"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputMultiUDPRules) ContainerAction(ctx context.Context, 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(ipv6, rules)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputMultiUDPRules) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // No-op.
- return nil
-}
-
-// FilterInputRequireProtocolUDP checks that "-m udp" requires "-p udp" to be
-// specified.
-type FilterInputRequireProtocolUDP struct{ baseCase }
-
-var _ TestCase = (*FilterInputRequireProtocolUDP)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputRequireProtocolUDP) Name() string {
- return "FilterInputRequireProtocolUDP"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputRequireProtocolUDP) ContainerAction(ctx context.Context, 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
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputRequireProtocolUDP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // No-op.
- return nil
-}
-
-// FilterInputCreateUserChain tests chain creation.
-type FilterInputCreateUserChain struct{ baseCase }
-
-var _ TestCase = (*FilterInputCreateUserChain)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputCreateUserChain) Name() string {
- return "FilterInputCreateUserChain"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputCreateUserChain) ContainerAction(ctx context.Context, 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(ipv6, rules)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputCreateUserChain) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // No-op.
- return nil
-}
-
-// FilterInputDefaultPolicyAccept tests the default ACCEPT policy.
-type FilterInputDefaultPolicyAccept struct{ containerCase }
-
-var _ TestCase = (*FilterInputDefaultPolicyAccept)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputDefaultPolicyAccept) Name() string {
- return "FilterInputDefaultPolicyAccept"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputDefaultPolicyAccept) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // Set the default policy to accept, then receive a packet.
- if err := filterTable(ipv6, "-P", "INPUT", "ACCEPT"); err != nil {
- return err
- }
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputDefaultPolicyAccept) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputDefaultPolicyDrop tests the default DROP policy.
-type FilterInputDefaultPolicyDrop struct{ containerCase }
-
-var _ TestCase = (*FilterInputDefaultPolicyDrop)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputDefaultPolicyDrop) Name() string {
- return "FilterInputDefaultPolicyDrop"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputDefaultPolicyDrop) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-P", "INPUT", "DROP"); err != nil {
- return err
- }
-
- // Listen for UDP packets on dropPort.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenUDP(timedCtx, dropPort, ipv6); err == nil {
- return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- 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(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputReturnUnderflow tests that -j RETURN in a built-in chain causes
-// the underflow rule (i.e. default policy) to be executed.
-type FilterInputReturnUnderflow struct{ containerCase }
-
-var _ TestCase = (*FilterInputReturnUnderflow)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputReturnUnderflow) Name() string {
- return "FilterInputReturnUnderflow"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputReturnUnderflow) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) 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(ipv6, rules); err != nil {
- return err
- }
-
- // We should receive packets, as the RETURN rule will trigger the default
- // ACCEPT policy.
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputReturnUnderflow) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputSerializeJump verifies that we can serialize jumps.
-type FilterInputSerializeJump struct{ baseCase }
-
-var _ TestCase = (*FilterInputSerializeJump)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputSerializeJump) Name() string {
- return "FilterInputSerializeJump"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputSerializeJump) ContainerAction(ctx context.Context, 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(ipv6, rules)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputSerializeJump) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // No-op.
- return nil
-}
-
-// FilterInputJumpBasic jumps to a chain and executes a rule there.
-type FilterInputJumpBasic struct{ containerCase }
-
-var _ TestCase = (*FilterInputJumpBasic)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputJumpBasic) Name() string {
- return "FilterInputJumpBasic"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputJumpBasic) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- rules := [][]string{
- {"-P", "INPUT", "DROP"},
- {"-N", chainName},
- {"-A", "INPUT", "-j", chainName},
- {"-A", chainName, "-j", "ACCEPT"},
- }
- if err := filterTableRules(ipv6, rules); err != nil {
- return err
- }
-
- // Listen for UDP packets on acceptPort.
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputJumpBasic) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputJumpReturn jumps, returns, and executes a rule.
-type FilterInputJumpReturn struct{ containerCase }
-
-var _ TestCase = (*FilterInputJumpReturn)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputJumpReturn) Name() string {
- return "FilterInputJumpReturn"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputJumpReturn) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- rules := [][]string{
- {"-N", chainName},
- {"-P", "INPUT", "ACCEPT"},
- {"-A", "INPUT", "-j", chainName},
- {"-A", chainName, "-j", "RETURN"},
- {"-A", chainName, "-j", "DROP"},
- }
- if err := filterTableRules(ipv6, rules); err != nil {
- return err
- }
-
- // Listen for UDP packets on acceptPort.
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputJumpReturn) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputJumpReturnDrop jumps to a chain, returns, and DROPs packets.
-type FilterInputJumpReturnDrop struct{ containerCase }
-
-var _ TestCase = (*FilterInputJumpReturnDrop)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputJumpReturnDrop) Name() string {
- return "FilterInputJumpReturnDrop"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputJumpReturnDrop) ContainerAction(ctx context.Context, 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(ipv6, rules); err != nil {
- return err
- }
-
- // Listen for UDP packets on dropPort.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenUDP(timedCtx, dropPort, ipv6); err == nil {
- return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- 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(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, dropPort, ipv6)
-}
-
-// FilterInputJumpBuiltin verifies that jumping to a top-levl chain is illegal.
-type FilterInputJumpBuiltin struct{ baseCase }
-
-var _ TestCase = (*FilterInputJumpBuiltin)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputJumpBuiltin) Name() string {
- return "FilterInputJumpBuiltin"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputJumpBuiltin) ContainerAction(ctx context.Context, 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(ctx context.Context, ip net.IP, ipv6 bool) error {
- // No-op.
- return nil
-}
-
-// FilterInputJumpTwice jumps twice, then returns twice and executes a rule.
-type FilterInputJumpTwice struct{ containerCase }
-
-var _ TestCase = (*FilterInputJumpTwice)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputJumpTwice) Name() string {
- return "FilterInputJumpTwice"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputJumpTwice) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) 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(ipv6, rules); err != nil {
- return err
- }
-
- // UDP packets should jump and return twice, eventually hitting the
- // ACCEPT rule.
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputJumpTwice) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputDestination verifies that we can filter packets via `-d
-// <ipaddr>`.
-type FilterInputDestination struct{ containerCase }
-
-var _ TestCase = (*FilterInputDestination)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputDestination) Name() string {
- return "FilterInputDestination"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputDestination) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- addrs, err := localAddrs(ipv6)
- 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(ipv6, rules); err != nil {
- return err
- }
-
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputDestination) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputInvertDestination verifies that we can filter packets via `! -d
-// <ipaddr>`.
-type FilterInputInvertDestination struct{ containerCase }
-
-var _ TestCase = (*FilterInputInvertDestination)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputInvertDestination) Name() string {
- return "FilterInputInvertDestination"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputInvertDestination) ContainerAction(ctx context.Context, 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(ipv6), "-j", "ACCEPT"},
- }
- if err := filterTableRules(ipv6, rules); err != nil {
- return err
- }
-
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputInvertDestination) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputSource verifies that we can filter packets via `-s
-// <ipaddr>`.
-type FilterInputSource struct{ containerCase }
-
-var _ TestCase = (*FilterInputSource)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputSource) Name() string {
- return "FilterInputSource"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputSource) ContainerAction(ctx context.Context, 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(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputSource) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputInvertSource verifies that we can filter packets via `! -s
-// <ipaddr>`.
-type FilterInputInvertSource struct{ containerCase }
-
-var _ TestCase = (*FilterInputInvertSource)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputInvertSource) Name() string {
- return "FilterInputInvertSource"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputInvertSource) ContainerAction(ctx context.Context, 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(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputInvertSource) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputInterfaceAccept tests that packets are accepted from interface
-// matching the iptables rule.
-type FilterInputInterfaceAccept struct{ localCase }
-
-var _ TestCase = (*FilterInputInterfaceAccept)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputInterfaceAccept) Name() string {
- return "FilterInputInterfaceAccept"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputInterfaceAccept) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- ifname, ok := getInterfaceName()
- if !ok {
- return fmt.Errorf("no interface is present, except loopback")
- }
- if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-i", ifname, "-j", "ACCEPT"); err != nil {
- return err
- }
- if err := listenUDP(ctx, acceptPort, ipv6); err != nil {
- return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %w", acceptPort, err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputInterfaceAccept) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputInterfaceDrop tests that packets are dropped from interface
-// matching the iptables rule.
-type FilterInputInterfaceDrop struct{ localCase }
-
-var _ TestCase = (*FilterInputInterfaceDrop)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputInterfaceDrop) Name() string {
- return "FilterInputInterfaceDrop"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputInterfaceDrop) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- ifname, ok := getInterfaceName()
- if !ok {
- return fmt.Errorf("no interface is present, except loopback")
- }
- if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-i", ifname, "-j", "DROP"); err != nil {
- return err
- }
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenUDP(timedCtx, acceptPort, ipv6); err != nil {
- if errors.Is(err, context.DeadlineExceeded) {
- return nil
- }
- return fmt.Errorf("error reading: %w", err)
- }
- return fmt.Errorf("packets should have been dropped, but got a packet")
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputInterfaceDrop) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputInterface tests that packets are not dropped from interface which
-// is not matching the interface name in the iptables rule.
-type FilterInputInterface struct{ localCase }
-
-var _ TestCase = (*FilterInputInterface)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputInterface) Name() string {
- return "FilterInputInterface"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputInterface) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-i", "lo", "-j", "DROP"); err != nil {
- return err
- }
- if err := listenUDP(ctx, acceptPort, ipv6); err != nil {
- return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %w", acceptPort, err)
- }
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputInterface) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputInterfaceBeginsWith tests that packets are dropped from an
-// interface which begins with the given interface name.
-type FilterInputInterfaceBeginsWith struct{ localCase }
-
-var _ TestCase = (*FilterInputInterfaceBeginsWith)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputInterfaceBeginsWith) Name() string {
- return "FilterInputInterfaceBeginsWith"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputInterfaceBeginsWith) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-i", "e+", "-j", "DROP"); err != nil {
- return err
- }
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenUDP(timedCtx, acceptPort, ipv6); err != nil {
- if errors.Is(err, context.DeadlineExceeded) {
- return nil
- }
- return fmt.Errorf("error reading: %w", err)
- }
- return fmt.Errorf("packets should have been dropped, but got a packet")
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputInterfaceBeginsWith) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterInputInterfaceInvertDrop tests that we selectively drop packets from
-// interface not matching the interface name.
-type FilterInputInterfaceInvertDrop struct{ baseCase }
-
-var _ TestCase = (*FilterInputInterfaceInvertDrop)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputInterfaceInvertDrop) Name() string {
- return "FilterInputInterfaceInvertDrop"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputInterfaceInvertDrop) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-A", "INPUT", "-p", "tcp", "!", "-i", "lo", "-j", "DROP"); err != nil {
- return err
- }
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenTCP(timedCtx, acceptPort, ipv6); err != nil {
- if errors.Is(err, context.DeadlineExceeded) {
- return nil
- }
- return fmt.Errorf("error reading: %w", err)
- }
- return fmt.Errorf("connection on port %d should not be accepted, but was accepted", acceptPort)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputInterfaceInvertDrop) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err != nil {
- var operr *net.OpError
- if errors.As(err, &operr) && operr.Timeout() {
- return nil
- }
- return fmt.Errorf("error connecting: %w", err)
- }
- return fmt.Errorf("connection destined to port %d should not be accepted, but was accepted", acceptPort)
-}
-
-// FilterInputInterfaceInvertAccept tests that we can selectively accept packets
-// not matching the specific incoming interface.
-type FilterInputInterfaceInvertAccept struct{ baseCase }
-
-var _ TestCase = (*FilterInputInterfaceInvertAccept)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterInputInterfaceInvertAccept) Name() string {
- return "FilterInputInterfaceInvertAccept"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterInputInterfaceInvertAccept) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-A", "INPUT", "-p", "tcp", "!", "-i", "lo", "-j", "ACCEPT"); err != nil {
- return err
- }
- return listenTCP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterInputInterfaceInvertAccept) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return connectTCP(ctx, ip, acceptPort, ipv6)
-}
diff --git a/test/iptables/filter_output.go b/test/iptables/filter_output.go
deleted file mode 100644
index bcb2a3b70..000000000
--- a/test/iptables/filter_output.go
+++ /dev/null
@@ -1,714 +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 (
- "context"
- "errors"
- "fmt"
- "net"
-)
-
-func init() {
- RegisterTestCase(&FilterOutputDropTCPDestPort{})
- RegisterTestCase(&FilterOutputDropTCPSrcPort{})
- RegisterTestCase(&FilterOutputDestination{})
- RegisterTestCase(&FilterOutputInvertDestination{})
- RegisterTestCase(&FilterOutputAcceptTCPOwner{})
- RegisterTestCase(&FilterOutputDropTCPOwner{})
- RegisterTestCase(&FilterOutputAcceptUDPOwner{})
- RegisterTestCase(&FilterOutputDropUDPOwner{})
- RegisterTestCase(&FilterOutputOwnerFail{})
- RegisterTestCase(&FilterOutputAcceptGIDOwner{})
- RegisterTestCase(&FilterOutputDropGIDOwner{})
- RegisterTestCase(&FilterOutputInvertGIDOwner{})
- RegisterTestCase(&FilterOutputInvertUIDOwner{})
- RegisterTestCase(&FilterOutputInvertUIDAndGIDOwner{})
- RegisterTestCase(&FilterOutputInterfaceAccept{})
- RegisterTestCase(&FilterOutputInterfaceDrop{})
- RegisterTestCase(&FilterOutputInterface{})
- RegisterTestCase(&FilterOutputInterfaceBeginsWith{})
- RegisterTestCase(&FilterOutputInterfaceInvertDrop{})
- RegisterTestCase(&FilterOutputInterfaceInvertAccept{})
-}
-
-// FilterOutputDropTCPDestPort tests that connections are not accepted on
-// specified source ports.
-type FilterOutputDropTCPDestPort struct{ baseCase }
-
-var _ TestCase = (*FilterOutputDropTCPDestPort)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputDropTCPDestPort) Name() string {
- return "FilterOutputDropTCPDestPort"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputDropTCPDestPort) ContainerAction(ctx context.Context, 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
- }
-
- // Listen for TCP packets on accept port.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", dropPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputDropTCPDestPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := connectTCP(timedCtx, ip, acceptPort, ipv6); 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{ baseCase }
-
-var _ TestCase = (*FilterOutputDropTCPSrcPort)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputDropTCPSrcPort) Name() string {
- return "FilterOutputDropTCPSrcPort"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputDropTCPSrcPort) ContainerAction(ctx context.Context, 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
- }
-
- // Listen for TCP packets on drop port.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenTCP(timedCtx, dropPort, ipv6); err == nil {
- return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputDropTCPSrcPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := connectTCP(timedCtx, ip, dropPort, ipv6); err == nil {
- return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", dropPort)
- }
-
- return nil
-}
-
-// FilterOutputAcceptTCPOwner tests that TCP connections from uid owner are accepted.
-type FilterOutputAcceptTCPOwner struct{ baseCase }
-
-var _ TestCase = (*FilterOutputAcceptTCPOwner)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputAcceptTCPOwner) Name() string {
- return "FilterOutputAcceptTCPOwner"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputAcceptTCPOwner) ContainerAction(ctx context.Context, 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
- }
-
- // Listen for TCP packets on accept port.
- return listenTCP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputAcceptTCPOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return connectTCP(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterOutputDropTCPOwner tests that TCP connections from uid owner are dropped.
-type FilterOutputDropTCPOwner struct{ baseCase }
-
-var _ TestCase = (*FilterOutputDropTCPOwner)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputDropTCPOwner) Name() string {
- return "FilterOutputDropTCPOwner"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputDropTCPOwner) ContainerAction(ctx context.Context, 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
- }
-
- // Listen for TCP packets on accept port.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection on port %d should be dropped, but got accepted", acceptPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputDropTCPOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection destined to port %d should be dropped, but got accepted", acceptPort)
- }
-
- return nil
-}
-
-// FilterOutputAcceptUDPOwner tests that UDP packets from uid owner are accepted.
-type FilterOutputAcceptUDPOwner struct{ localCase }
-
-var _ TestCase = (*FilterOutputAcceptUDPOwner)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputAcceptUDPOwner) Name() string {
- return "FilterOutputAcceptUDPOwner"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputAcceptUDPOwner) ContainerAction(ctx context.Context, 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
- }
-
- // Send UDP packets on acceptPort.
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputAcceptUDPOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // Listen for UDP packets on acceptPort.
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// FilterOutputDropUDPOwner tests that UDP packets from uid owner are dropped.
-type FilterOutputDropUDPOwner struct{ localCase }
-
-var _ TestCase = (*FilterOutputDropUDPOwner)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputDropUDPOwner) Name() string {
- return "FilterOutputDropUDPOwner"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputDropUDPOwner) ContainerAction(ctx context.Context, 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
- }
-
- // Send UDP packets on dropPort.
- return sendUDPLoop(ctx, ip, dropPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputDropUDPOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // Listen for UDP packets on dropPort.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenUDP(timedCtx, dropPort, ipv6); err == nil {
- return fmt.Errorf("packets should not be received")
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// FilterOutputOwnerFail tests that without uid/gid option, owner rule
-// will fail.
-type FilterOutputOwnerFail struct{ baseCase }
-
-var _ TestCase = (*FilterOutputOwnerFail)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputOwnerFail) Name() string {
- return "FilterOutputOwnerFail"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputOwnerFail) ContainerAction(ctx context.Context, 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")
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputOwnerFail) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // no-op.
- return nil
-}
-
-// FilterOutputAcceptGIDOwner tests that TCP connections from gid owner are accepted.
-type FilterOutputAcceptGIDOwner struct{ baseCase }
-
-var _ TestCase = (*FilterOutputAcceptGIDOwner)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputAcceptGIDOwner) Name() string {
- return "FilterOutputAcceptGIDOwner"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputAcceptGIDOwner) ContainerAction(ctx context.Context, 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
- }
-
- // Listen for TCP packets on accept port.
- return listenTCP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputAcceptGIDOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return connectTCP(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterOutputDropGIDOwner tests that TCP connections from gid owner are dropped.
-type FilterOutputDropGIDOwner struct{ baseCase }
-
-var _ TestCase = (*FilterOutputDropGIDOwner)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputDropGIDOwner) Name() string {
- return "FilterOutputDropGIDOwner"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputDropGIDOwner) ContainerAction(ctx context.Context, 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
- }
-
- // Listen for TCP packets on accept port.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection on port %d should not be accepted, but got accepted", acceptPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputDropGIDOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort)
- }
-
- return nil
-}
-
-// FilterOutputInvertGIDOwner tests that TCP connections from gid owner are dropped.
-type FilterOutputInvertGIDOwner struct{ baseCase }
-
-var _ TestCase = (*FilterOutputInvertGIDOwner)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputInvertGIDOwner) Name() string {
- return "FilterOutputInvertGIDOwner"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputInvertGIDOwner) ContainerAction(ctx context.Context, 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(ipv6, rules); err != nil {
- return err
- }
-
- // Listen for TCP packets on accept port.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection on port %d should not be accepted, but got accepted", acceptPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputInvertGIDOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort)
- }
-
- return nil
-}
-
-// FilterOutputInvertUIDOwner tests that TCP connections from gid owner are dropped.
-type FilterOutputInvertUIDOwner struct{ baseCase }
-
-var _ TestCase = (*FilterOutputInvertUIDOwner)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputInvertUIDOwner) Name() string {
- return "FilterOutputInvertUIDOwner"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputInvertUIDOwner) ContainerAction(ctx context.Context, 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(ipv6, rules); err != nil {
- return err
- }
-
- // Listen for TCP packets on accept port.
- return listenTCP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputInvertUIDOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return connectTCP(ctx, ip, acceptPort, ipv6)
-}
-
-// FilterOutputInvertUIDAndGIDOwner tests that TCP connections from uid and gid
-// owner are dropped.
-type FilterOutputInvertUIDAndGIDOwner struct{ baseCase }
-
-var _ TestCase = (*FilterOutputInvertUIDAndGIDOwner)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputInvertUIDAndGIDOwner) Name() string {
- return "FilterOutputInvertUIDAndGIDOwner"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputInvertUIDAndGIDOwner) ContainerAction(ctx context.Context, 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(ipv6, rules); err != nil {
- return err
- }
-
- // Listen for TCP packets on accept port.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection on port %d should not be accepted, but got accepted", acceptPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputInvertUIDAndGIDOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort)
- }
-
- return nil
-}
-
-// FilterOutputDestination tests that we can selectively allow packets to
-// certain destinations.
-type FilterOutputDestination struct{ localCase }
-
-var _ TestCase = (*FilterOutputDestination)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputDestination) Name() string {
- return "FilterOutputDestination"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputDestination) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- var rules [][]string
- if ipv6 {
- rules = [][]string{
- {"-A", "OUTPUT", "-d", ip.String(), "-j", "ACCEPT"},
- // Allow solicited node multicast addresses so we can send neighbor
- // solicitations.
- {"-A", "OUTPUT", "-d", "ff02::1:ff00:0/104", "-j", "ACCEPT"},
- {"-P", "OUTPUT", "DROP"},
- }
- } else {
- rules = [][]string{
- {"-A", "OUTPUT", "-d", ip.String(), "-j", "ACCEPT"},
- {"-P", "OUTPUT", "DROP"},
- }
- }
- if err := filterTableRules(ipv6, rules); err != nil {
- return err
- }
-
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputDestination) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// FilterOutputInvertDestination tests that we can selectively allow packets
-// not headed for a particular destination.
-type FilterOutputInvertDestination struct{ localCase }
-
-var _ TestCase = (*FilterOutputInvertDestination)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputInvertDestination) Name() string {
- return "FilterOutputInvertDestination"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputInvertDestination) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- rules := [][]string{
- {"-A", "OUTPUT", "!", "-d", localIP(ipv6), "-j", "ACCEPT"},
- {"-P", "OUTPUT", "DROP"},
- }
- if err := filterTableRules(ipv6, rules); err != nil {
- return err
- }
-
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputInvertDestination) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// FilterOutputInterfaceAccept tests that packets are sent via interface
-// matching the iptables rule.
-type FilterOutputInterfaceAccept struct{ localCase }
-
-var _ TestCase = (*FilterOutputInterfaceAccept)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputInterfaceAccept) Name() string {
- return "FilterOutputInterfaceAccept"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputInterfaceAccept) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- ifname, ok := getInterfaceName()
- if !ok {
- return fmt.Errorf("no interface is present, except loopback")
- }
- if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", ifname, "-j", "ACCEPT"); err != nil {
- return err
- }
-
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputInterfaceAccept) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// FilterOutputInterfaceDrop tests that packets are not sent via interface
-// matching the iptables rule.
-type FilterOutputInterfaceDrop struct{ localCase }
-
-var _ TestCase = (*FilterOutputInterfaceDrop)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputInterfaceDrop) Name() string {
- return "FilterOutputInterfaceDrop"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputInterfaceDrop) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- ifname, ok := getInterfaceName()
- if !ok {
- return fmt.Errorf("no interface is present, except loopback")
- }
- if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", ifname, "-j", "DROP"); err != nil {
- return err
- }
-
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputInterfaceDrop) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenUDP(timedCtx, acceptPort, ipv6); err == nil {
- return fmt.Errorf("packets should not be received on port %v, but are received", acceptPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// FilterOutputInterface tests that packets are sent via interface which is
-// not matching the interface name in the iptables rule.
-type FilterOutputInterface struct{ localCase }
-
-var _ TestCase = (*FilterOutputInterface)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputInterface) Name() string {
- return "FilterOutputInterface"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputInterface) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", "lo", "-j", "DROP"); err != nil {
- return err
- }
-
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputInterface) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// FilterOutputInterfaceBeginsWith tests that packets are not sent via an
-// interface which begins with the given interface name.
-type FilterOutputInterfaceBeginsWith struct{ localCase }
-
-var _ TestCase = (*FilterOutputInterfaceBeginsWith)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputInterfaceBeginsWith) Name() string {
- return "FilterOutputInterfaceBeginsWith"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputInterfaceBeginsWith) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", "e+", "-j", "DROP"); err != nil {
- return err
- }
-
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputInterfaceBeginsWith) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenUDP(timedCtx, acceptPort, ipv6); err == nil {
- return fmt.Errorf("packets should not be received on port %v, but are received", acceptPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// FilterOutputInterfaceInvertDrop tests that we selectively do not send
-// packets via interface not matching the interface name.
-type FilterOutputInterfaceInvertDrop struct{ baseCase }
-
-var _ TestCase = (*FilterOutputInterfaceInvertDrop)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputInterfaceInvertDrop) Name() string {
- return "FilterOutputInterfaceInvertDrop"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputInterfaceInvertDrop) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "!", "-o", "lo", "-j", "DROP"); err != nil {
- return err
- }
-
- // Listen for TCP packets on accept port.
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection on port %d should not be accepted, but got accepted", acceptPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputInterfaceInvertDrop) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err == nil {
- return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort)
- }
-
- return nil
-}
-
-// FilterOutputInterfaceInvertAccept tests that we can selectively send packets
-// not matching the specific outgoing interface.
-type FilterOutputInterfaceInvertAccept struct{ baseCase }
-
-var _ TestCase = (*FilterOutputInterfaceInvertAccept)(nil)
-
-// Name implements TestCase.Name.
-func (*FilterOutputInterfaceInvertAccept) Name() string {
- return "FilterOutputInterfaceInvertAccept"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*FilterOutputInterfaceInvertAccept) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "!", "-o", "lo", "-j", "ACCEPT"); err != nil {
- return err
- }
-
- // Listen for TCP packets on accept port.
- return listenTCP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*FilterOutputInterfaceInvertAccept) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return connectTCP(ctx, ip, acceptPort, ipv6)
-}
diff --git a/test/iptables/iptables.go b/test/iptables/iptables.go
deleted file mode 100644
index 970587a02..000000000
--- a/test/iptables/iptables.go
+++ /dev/null
@@ -1,115 +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 (
- "context"
- "fmt"
- "net"
- "time"
-)
-
-// IPExchangePort is the port the container listens on to receive the IP
-// address of the local process.
-const IPExchangePort = 2349
-
-// TerminalStatement is the last statement in the test runner.
-const TerminalStatement = "Finished!"
-
-// TestTimeout is the timeout used for all tests.
-const TestTimeout = 10 * time.Second
-
-// NegativeTimeout is the time tests should wait to establish the negative
-// case, i.e. that connections are not made.
-const NegativeTimeout = 2 * time.Second
-
-// 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(ctx context.Context, ip net.IP, ipv6 bool) error
-
- // LocalAction runs locally. It receives the IP of the container.
- LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error
-
- // ContainerSufficient indicates whether ContainerAction's return value
- // alone indicates whether the test succeeded.
- ContainerSufficient() bool
-
- // LocalSufficient indicates whether LocalAction's return value alone
- // indicates whether the test succeeded.
- LocalSufficient() bool
-}
-
-// baseCase provides defaults for ContainerSufficient and LocalSufficient when
-// both actions are required to finish.
-type baseCase struct{}
-
-// ContainerSufficient implements TestCase.ContainerSufficient.
-func (*baseCase) ContainerSufficient() bool {
- return false
-}
-
-// LocalSufficient implements TestCase.LocalSufficient.
-func (*baseCase) LocalSufficient() bool {
- return false
-}
-
-// localCase provides defaults for ContainerSufficient and LocalSufficient when
-// only the local action is required to finish.
-type localCase struct{}
-
-// ContainerSufficient implements TestCase.ContainerSufficient.
-func (*localCase) ContainerSufficient() bool {
- return false
-}
-
-// LocalSufficient implements TestCase.LocalSufficient.
-func (*localCase) LocalSufficient() bool {
- return true
-}
-
-// containerCase provides defaults for ContainerSufficient and LocalSufficient
-// when only the container action is required to finish.
-type containerCase struct{}
-
-// ContainerSufficient implements TestCase.ContainerSufficient.
-func (*containerCase) ContainerSufficient() bool {
- return true
-}
-
-// LocalSufficient implements TestCase.LocalSufficient.
-func (*containerCase) LocalSufficient() bool {
- return false
-}
-
-// 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 d6c69a319..000000000
--- a/test/iptables/iptables_test.go
+++ /dev/null
@@ -1,458 +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 (
- "context"
- "errors"
- "fmt"
- "net"
- "reflect"
- "sync"
- "testing"
-
- "gvisor.dev/gvisor/pkg/test/dockerutil"
- "gvisor.dev/gvisor/pkg/test/testutil"
-)
-
-// 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(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())
- }
-
- // Wait for the local and container goroutines to finish.
- var wg sync.WaitGroup
- defer wg.Wait()
-
- ctx, cancel := context.WithTimeout(context.Background(), TestTimeout)
- defer cancel()
-
- d := dockerutil.MakeContainer(ctx, t)
- defer func() {
- if logs, err := d.Logs(context.Background()); err != nil {
- t.Logf("Failed to retrieve container logs.")
- } else {
- t.Logf("=== Container logs: ===\n%s", logs)
- }
- // Use a new context, as cleanup should run even when we
- // timeout.
- d.CleanUp(context.Background())
- }()
-
- // Create and start the container.
- opts := dockerutil.RunOpts{
- Image: "iptables",
- CapAdd: []string{"NET_ADMIN"},
- }
- 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(ctx, ipv6)
- if err != nil {
- // If ipv6 is not configured, don't fail.
- if ipv6 && err == dockerutil.ErrNoIP {
- t.Skipf("No ipv6 address is available.")
- }
- t.Fatalf("failed to get container IP: %v", err)
- }
-
- // Give the container our IP.
- if err := sendIP(ip); err != nil {
- t.Fatalf("failed to send IP to container: %v", err)
- }
-
- // Run our side of the test.
- errCh := make(chan error, 2)
- wg.Add(1)
- go func() {
- defer wg.Done()
- if err := test.LocalAction(ctx, ip, ipv6); err != nil && !errors.Is(err, context.Canceled) {
- errCh <- fmt.Errorf("LocalAction failed: %v", err)
- } else {
- errCh <- nil
- }
- if test.LocalSufficient() {
- errCh <- nil
- }
- }()
-
- // Run the container side.
- wg.Add(1)
- go func() {
- defer wg.Done()
- // 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(ctx, TerminalStatement, TestTimeout); err != nil && !errors.Is(err, context.Canceled) {
- errCh <- fmt.Errorf("ContainerAction failed: %v", err)
- } else {
- errCh <- nil
- }
- if test.ContainerSufficient() {
- errCh <- nil
- }
- }()
-
- for i := 0; i < 2; i++ {
- select {
- case err := <-errCh:
- if err != nil {
- t.Fatal(err)
- }
- }
- }
-}
-
-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("tcp", nil, &contAddr)
- conn = c
- return err
- }
- if err := testutil.Poll(cb, TestTimeout); 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 TestFilterInputDropUDP(t *testing.T) {
- singleTest(t, &FilterInputDropUDP{})
-}
-
-func TestFilterInputDropUDPPort(t *testing.T) {
- singleTest(t, &FilterInputDropUDPPort{})
-}
-
-func TestFilterInputDropDifferentUDPPort(t *testing.T) {
- singleTest(t, &FilterInputDropDifferentUDPPort{})
-}
-
-func TestFilterInputDropAll(t *testing.T) {
- singleTest(t, &FilterInputDropAll{})
-}
-
-func TestFilterInputDropOnlyUDP(t *testing.T) {
- singleTest(t, &FilterInputDropOnlyUDP{})
-}
-
-func TestFilterInputDropTCPDestPort(t *testing.T) {
- singleTest(t, &FilterInputDropTCPDestPort{})
-}
-
-func TestFilterInputDropTCPSrcPort(t *testing.T) {
- singleTest(t, &FilterInputDropTCPSrcPort{})
-}
-
-func TestFilterInputCreateUserChain(t *testing.T) {
- singleTest(t, &FilterInputCreateUserChain{})
-}
-
-func TestFilterInputDefaultPolicyAccept(t *testing.T) {
- singleTest(t, &FilterInputDefaultPolicyAccept{})
-}
-
-func TestFilterInputDefaultPolicyDrop(t *testing.T) {
- singleTest(t, &FilterInputDefaultPolicyDrop{})
-}
-
-func TestFilterInputReturnUnderflow(t *testing.T) {
- singleTest(t, &FilterInputReturnUnderflow{})
-}
-
-func TestFilterOutputDropTCPDestPort(t *testing.T) {
- singleTest(t, &FilterOutputDropTCPDestPort{})
-}
-
-func TestFilterOutputDropTCPSrcPort(t *testing.T) {
- singleTest(t, &FilterOutputDropTCPSrcPort{})
-}
-
-func TestFilterOutputAcceptTCPOwner(t *testing.T) {
- singleTest(t, &FilterOutputAcceptTCPOwner{})
-}
-
-func TestFilterOutputDropTCPOwner(t *testing.T) {
- singleTest(t, &FilterOutputDropTCPOwner{})
-}
-
-func TestFilterOutputAcceptUDPOwner(t *testing.T) {
- singleTest(t, &FilterOutputAcceptUDPOwner{})
-}
-
-func TestFilterOutputDropUDPOwner(t *testing.T) {
- singleTest(t, &FilterOutputDropUDPOwner{})
-}
-
-func TestFilterOutputOwnerFail(t *testing.T) {
- singleTest(t, &FilterOutputOwnerFail{})
-}
-
-func TestFilterOutputAcceptGIDOwner(t *testing.T) {
- singleTest(t, &FilterOutputAcceptGIDOwner{})
-}
-
-func TestFilterOutputDropGIDOwner(t *testing.T) {
- singleTest(t, &FilterOutputDropGIDOwner{})
-}
-
-func TestFilterOutputInvertGIDOwner(t *testing.T) {
- singleTest(t, &FilterOutputInvertGIDOwner{})
-}
-
-func TestFilterOutputInvertUIDOwner(t *testing.T) {
- singleTest(t, &FilterOutputInvertUIDOwner{})
-}
-
-func TestFilterOutputInvertUIDAndGIDOwner(t *testing.T) {
- singleTest(t, &FilterOutputInvertUIDAndGIDOwner{})
-}
-
-func TestFilterOutputInterfaceAccept(t *testing.T) {
- singleTest(t, &FilterOutputInterfaceAccept{})
-}
-
-func TestFilterOutputInterfaceDrop(t *testing.T) {
- singleTest(t, &FilterOutputInterfaceDrop{})
-}
-
-func TestFilterOutputInterface(t *testing.T) {
- singleTest(t, &FilterOutputInterface{})
-}
-
-func TestFilterOutputInterfaceBeginsWith(t *testing.T) {
- singleTest(t, &FilterOutputInterfaceBeginsWith{})
-}
-
-func TestFilterOutputInterfaceInvertDrop(t *testing.T) {
- singleTest(t, &FilterOutputInterfaceInvertDrop{})
-}
-
-func TestFilterOutputInterfaceInvertAccept(t *testing.T) {
- singleTest(t, &FilterOutputInterfaceInvertAccept{})
-}
-
-func TestJumpSerialize(t *testing.T) {
- singleTest(t, &FilterInputSerializeJump{})
-}
-
-func TestJumpBasic(t *testing.T) {
- singleTest(t, &FilterInputJumpBasic{})
-}
-
-func TestJumpReturn(t *testing.T) {
- singleTest(t, &FilterInputJumpReturn{})
-}
-
-func TestJumpReturnDrop(t *testing.T) {
- singleTest(t, &FilterInputJumpReturnDrop{})
-}
-
-func TestJumpBuiltin(t *testing.T) {
- singleTest(t, &FilterInputJumpBuiltin{})
-}
-
-func TestJumpTwice(t *testing.T) {
- singleTest(t, &FilterInputJumpTwice{})
-}
-
-func TestInputDestination(t *testing.T) {
- singleTest(t, &FilterInputDestination{})
-}
-
-func TestInputInvertDestination(t *testing.T) {
- singleTest(t, &FilterInputInvertDestination{})
-}
-
-func TestFilterOutputDestination(t *testing.T) {
- singleTest(t, &FilterOutputDestination{})
-}
-
-func TestFilterOutputInvertDestination(t *testing.T) {
- singleTest(t, &FilterOutputInvertDestination{})
-}
-
-func TestNATPreRedirectUDPPort(t *testing.T) {
- singleTest(t, &NATPreRedirectUDPPort{})
-}
-
-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{})
-}
-
-func TestNATOutRedirectTCPPort(t *testing.T) {
- singleTest(t, &NATOutRedirectTCPPort{})
-}
-
-func TestNATDropUDP(t *testing.T) {
- singleTest(t, &NATDropUDP{})
-}
-
-func TestNATAcceptAll(t *testing.T) {
- singleTest(t, &NATAcceptAll{})
-}
-
-func TestNATOutRedirectIP(t *testing.T) {
- singleTest(t, &NATOutRedirectIP{})
-}
-
-func TestNATOutDontRedirectIP(t *testing.T) {
- singleTest(t, &NATOutDontRedirectIP{})
-}
-
-func TestNATOutRedirectInvert(t *testing.T) {
- singleTest(t, &NATOutRedirectInvert{})
-}
-
-func TestNATPreRedirectIP(t *testing.T) {
- singleTest(t, &NATPreRedirectIP{})
-}
-
-func TestNATPreDontRedirectIP(t *testing.T) {
- singleTest(t, &NATPreDontRedirectIP{})
-}
-
-func TestNATPreRedirectInvert(t *testing.T) {
- singleTest(t, &NATPreRedirectInvert{})
-}
-
-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 TestInputInterfaceAccept(t *testing.T) {
- singleTest(t, &FilterInputInterfaceAccept{})
-}
-
-func TestInputInterfaceDrop(t *testing.T) {
- singleTest(t, &FilterInputInterfaceDrop{})
-}
-
-func TestInputInterface(t *testing.T) {
- singleTest(t, &FilterInputInterface{})
-}
-
-func TestInputInterfaceBeginsWith(t *testing.T) {
- singleTest(t, &FilterInputInterfaceBeginsWith{})
-}
-
-func TestInputInterfaceInvertDrop(t *testing.T) {
- singleTest(t, &FilterInputInterfaceInvertDrop{})
-}
-
-func TestInputInterfaceInvertAccept(t *testing.T) {
- singleTest(t, &FilterInputInterfaceInvertAccept{})
-}
-
-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{})
-}
-
-func TestNATPreRECVORIGDSTADDR(t *testing.T) {
- singleTest(t, &NATPreRECVORIGDSTADDR{})
-}
-
-func TestNATOutRECVORIGDSTADDR(t *testing.T) {
- singleTest(t, &NATOutRECVORIGDSTADDR{})
-}
diff --git a/test/iptables/iptables_unsafe.go b/test/iptables/iptables_unsafe.go
deleted file mode 100644
index dd1a1c082..000000000
--- a/test/iptables/iptables_unsafe.go
+++ /dev/null
@@ -1,64 +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"
- "unsafe"
-
- "golang.org/x/sys/unix"
-)
-
-type originalDstError struct {
- errno unix.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) (unix.RawSockaddrInet4, error) {
- var addr unix.RawSockaddrInet4
- var addrLen uint32 = unix.SizeofSockaddrInet4
- if errno := originalDestination(connfd, unix.SOL_IP, unsafe.Pointer(&addr), &addrLen); errno != 0 {
- return unix.RawSockaddrInet4{}, originalDstError{errno}
- }
- return addr, nil
-}
-
-func originalDestination6(connfd int) (unix.RawSockaddrInet6, error) {
- var addr unix.RawSockaddrInet6
- var addrLen uint32 = unix.SizeofSockaddrInet6
- if errno := originalDestination(connfd, unix.SOL_IPV6, unsafe.Pointer(&addr), &addrLen); errno != 0 {
- return unix.RawSockaddrInet6{}, originalDstError{errno}
- }
- return addr, nil
-}
-
-func originalDestination(connfd int, level uintptr, optval unsafe.Pointer, optlen *uint32) unix.Errno {
- _, _, errno := unix.Syscall6(
- unix.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
deleted file mode 100644
index bba17b894..000000000
--- a/test/iptables/iptables_util.go
+++ /dev/null
@@ -1,298 +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 (
- "context"
- "encoding/binary"
- "errors"
- "fmt"
- "net"
- "os/exec"
- "strings"
- "time"
-
- "gvisor.dev/gvisor/pkg/test/testutil"
-)
-
-// 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 `ip{6}tables -t nat` with the given args.
-func natTable(ipv6 bool, args ...string) error {
- return tableCmd(ipv6, "nat", args)
-}
-
-func tableCmd(ipv6 bool, table string, args []string) error {
- args = append([]string{"-t", table}, 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))
- }
- return nil
-}
-
-// filterTableRules is like filterTable, but runs multiple iptables commands.
-func filterTableRules(ipv6 bool, argsList [][]string) error {
- return tableRules(ipv6, "filter", argsList)
-}
-
-// natTableRules is like natTable, but runs multiple iptables commands.
-func natTableRules(ipv6 bool, argsList [][]string) error {
- return tableRules(ipv6, "nat", argsList)
-}
-
-func tableRules(ipv6 bool, table string, argsList [][]string) error {
- for _, args := range argsList {
- if err := tableCmd(ipv6, 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(ctx context.Context, port int, ipv6 bool) error {
- localAddr := net.UDPAddr{
- Port: port,
- }
- conn, err := net.ListenUDP(udpNetwork(ipv6), &localAddr)
- if err != nil {
- return err
- }
- defer conn.Close()
-
- ch := make(chan error)
- go func() {
- _, err = conn.Read([]byte{0})
- ch <- err
- }()
-
- select {
- case err := <-ch:
- return err
- case <-ctx.Done():
- return ctx.Err()
- }
-}
-
-// sendUDPLoop sends 1 byte UDP packets repeatedly to the IP and port specified
-// over a duration.
-func sendUDPLoop(ctx context.Context, ip net.IP, port int, ipv6 bool) error {
- remote := net.UDPAddr{
- IP: ip,
- Port: port,
- }
- conn, err := net.DialUDP(udpNetwork(ipv6), nil, &remote)
- if err != nil {
- return err
- }
- defer conn.Close()
-
- for {
- // 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 <-ctx.Done():
- // Being cancelled or timing out isn't an error, as we
- // cannot tell with UDP whether we succeeded.
- return nil
- // Continue looping.
- case <-time.After(200 * time.Millisecond):
- }
- }
-}
-
-// listenTCP listens for connections on a TCP port.
-func listenTCP(ctx context.Context, port int, ipv6 bool) error {
- localAddr := net.TCPAddr{
- Port: port,
- }
-
- // Starts listening on port.
- lConn, err := net.ListenTCP(tcpNetwork(ipv6), &localAddr)
- if err != nil {
- return err
- }
- defer lConn.Close()
-
- // Accept connections on port.
- ch := make(chan error)
- go func() {
- conn, err := lConn.AcceptTCP()
- ch <- err
- conn.Close()
- }()
-
- select {
- case err := <-ch:
- return err
- case <-ctx.Done():
- return fmt.Errorf("timed out waiting for a connection at %#v: %w", localAddr, ctx.Err())
- }
-}
-
-// connectTCP connects to the given IP and port from an ephemeral local address.
-func connectTCP(ctx context.Context, ip net.IP, port int, ipv6 bool) 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 {
- var d net.Dialer
- conn, err := d.DialContext(ctx, tcpNetwork(ipv6), contAddr.String())
- if conn != nil {
- conn.Close()
- }
- return err
- }
- if err := testutil.PollContext(ctx, callback); err != nil {
- return fmt.Errorf("timed out waiting to connect IP on port %v, most recent error: %w", port, err)
- }
-
- return nil
-}
-
-// 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 {
- // 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
-}
-
-// getInterfaceName returns the name of the interface other than loopback.
-func getInterfaceName() (string, bool) {
- 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" {
- 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"
-}
-
-func nowhereIP(ipv6 bool) string {
- if ipv6 {
- return "2001:db8::1"
- }
- return "192.0.2.1"
-}
-
-// udpNetwork returns an IPv6 or IPv6 UDP network argument to net.Dial.
-func udpNetwork(ipv6 bool) string {
- if ipv6 {
- return "udp6"
- }
- return "udp4"
-}
-
-// tcpNetwork returns an IPv6 or IPv6 TCP network argument to net.Dial.
-func tcpNetwork(ipv6 bool) string {
- if ipv6 {
- return "tcp6"
- }
- return "tcp4"
-}
diff --git a/test/iptables/nat.go b/test/iptables/nat.go
deleted file mode 100644
index 70d8a1832..000000000
--- a/test/iptables/nat.go
+++ /dev/null
@@ -1,917 +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 (
- "context"
- "errors"
- "fmt"
- "net"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/binary"
- "gvisor.dev/gvisor/pkg/usermem"
-)
-
-const redirectPort = 42
-
-func init() {
- RegisterTestCase(&NATPreRedirectUDPPort{})
- RegisterTestCase(&NATPreRedirectTCPPort{})
- RegisterTestCase(&NATPreRedirectTCPOutgoing{})
- RegisterTestCase(&NATOutRedirectTCPIncoming{})
- RegisterTestCase(&NATOutRedirectUDPPort{})
- RegisterTestCase(&NATOutRedirectTCPPort{})
- RegisterTestCase(&NATDropUDP{})
- RegisterTestCase(&NATAcceptAll{})
- RegisterTestCase(&NATPreRedirectIP{})
- RegisterTestCase(&NATPreDontRedirectIP{})
- RegisterTestCase(&NATPreRedirectInvert{})
- RegisterTestCase(&NATOutRedirectIP{})
- RegisterTestCase(&NATOutDontRedirectIP{})
- RegisterTestCase(&NATOutRedirectInvert{})
- RegisterTestCase(&NATRedirectRequiresProtocol{})
- RegisterTestCase(&NATLoopbackSkipsPrerouting{})
- RegisterTestCase(&NATPreOriginalDst{})
- RegisterTestCase(&NATOutOriginalDst{})
- RegisterTestCase(&NATPreRECVORIGDSTADDR{})
- RegisterTestCase(&NATOutRECVORIGDSTADDR{})
-}
-
-// NATPreRedirectUDPPort tests that packets are redirected to different port.
-type NATPreRedirectUDPPort struct{ containerCase }
-
-var _ TestCase = (*NATPreRedirectUDPPort)(nil)
-
-// Name implements TestCase.Name.
-func (*NATPreRedirectUDPPort) Name() string {
- return "NATPreRedirectUDPPort"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATPreRedirectUDPPort) ContainerAction(ctx context.Context, 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
- }
-
- if err := listenUDP(ctx, redirectPort, ipv6); 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 (*NATPreRedirectUDPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// NATPreRedirectTCPPort tests that connections are redirected on specified ports.
-type NATPreRedirectTCPPort struct{ baseCase }
-
-var _ TestCase = (*NATPreRedirectTCPPort)(nil)
-
-// Name implements TestCase.Name.
-func (*NATPreRedirectTCPPort) Name() string {
- return "NATPreRedirectTCPPort"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATPreRedirectTCPPort) ContainerAction(ctx context.Context, 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
- }
-
- // Listen for TCP packets on redirect port.
- return listenTCP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATPreRedirectTCPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return connectTCP(ctx, ip, dropPort, ipv6)
-}
-
-// NATPreRedirectTCPOutgoing verifies that outgoing TCP connections aren't
-// affected by PREROUTING connection tracking.
-type NATPreRedirectTCPOutgoing struct{ baseCase }
-
-var _ TestCase = (*NATPreRedirectTCPOutgoing)(nil)
-
-// Name implements TestCase.Name.
-func (*NATPreRedirectTCPOutgoing) Name() string {
- return "NATPreRedirectTCPOutgoing"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATPreRedirectTCPOutgoing) ContainerAction(ctx context.Context, 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(ctx, ip, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATPreRedirectTCPOutgoing) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return listenTCP(ctx, acceptPort, ipv6)
-}
-
-// NATOutRedirectTCPIncoming verifies that incoming TCP connections aren't
-// affected by OUTPUT connection tracking.
-type NATOutRedirectTCPIncoming struct{ baseCase }
-
-var _ TestCase = (*NATOutRedirectTCPIncoming)(nil)
-
-// Name implements TestCase.Name.
-func (*NATOutRedirectTCPIncoming) Name() string {
- return "NATOutRedirectTCPIncoming"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATOutRedirectTCPIncoming) ContainerAction(ctx context.Context, 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(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATOutRedirectTCPIncoming) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return connectTCP(ctx, ip, acceptPort, ipv6)
-}
-
-// NATOutRedirectUDPPort tests that packets are redirected to different port.
-type NATOutRedirectUDPPort struct{ containerCase }
-
-var _ TestCase = (*NATOutRedirectUDPPort)(nil)
-
-// Name implements TestCase.Name.
-func (*NATOutRedirectUDPPort) Name() string {
- return "NATOutRedirectUDPPort"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATOutRedirectUDPPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return loopbackTest(ctx, ipv6, net.ParseIP(nowhereIP(ipv6)), "-A", "OUTPUT", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort))
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATOutRedirectUDPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // No-op.
- return nil
-}
-
-// NATDropUDP tests that packets are not received in ports other than redirect
-// port.
-type NATDropUDP struct{ containerCase }
-
-var _ TestCase = (*NATDropUDP)(nil)
-
-// Name implements TestCase.Name.
-func (*NATDropUDP) Name() string {
- return "NATDropUDP"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATDropUDP) ContainerAction(ctx context.Context, 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
- }
-
- timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
- defer cancel()
- if err := listenUDP(timedCtx, acceptPort, ipv6); err == nil {
- return fmt.Errorf("packets on port %d should have been redirected to port %d", acceptPort, redirectPort)
- } else if !errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("error reading: %v", err)
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATDropUDP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// NATAcceptAll tests that all UDP packets are accepted.
-type NATAcceptAll struct{ containerCase }
-
-var _ TestCase = (*NATAcceptAll)(nil)
-
-// Name implements TestCase.Name.
-func (*NATAcceptAll) Name() string {
- return "NATAcceptAll"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATAcceptAll) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "ACCEPT"); err != nil {
- return err
- }
-
- if err := listenUDP(ctx, acceptPort, ipv6); 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(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// NATOutRedirectIP uses iptables to select packets based on destination IP and
-// redirects them.
-type NATOutRedirectIP struct{ baseCase }
-
-var _ TestCase = (*NATOutRedirectIP)(nil)
-
-// Name implements TestCase.Name.
-func (*NATOutRedirectIP) Name() string {
- return "NATOutRedirectIP"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATOutRedirectIP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // Redirect OUTPUT packets to a listening localhost port.
- return loopbackTest(ctx, 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(ctx context.Context, ip net.IP, ipv6 bool) error {
- // No-op.
- return nil
-}
-
-// NATOutDontRedirectIP tests that iptables matching with "-d" does not match
-// packets it shouldn't.
-type NATOutDontRedirectIP struct{ localCase }
-
-var _ TestCase = (*NATOutDontRedirectIP)(nil)
-
-// Name implements TestCase.Name.
-func (*NATOutDontRedirectIP) Name() string {
- return "NATOutDontRedirectIP"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATOutDontRedirectIP) ContainerAction(ctx context.Context, 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(ctx, ip, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATOutDontRedirectIP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// NATOutRedirectInvert tests that iptables can match with "! -d".
-type NATOutRedirectInvert struct{ baseCase }
-
-var _ TestCase = (*NATOutRedirectInvert)(nil)
-
-// Name implements TestCase.Name.
-func (*NATOutRedirectInvert) Name() string {
- return "NATOutRedirectInvert"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATOutRedirectInvert) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // Redirect OUTPUT packets to a listening localhost port.
- dest := "192.0.2.2"
- if ipv6 {
- dest = "2001:db8::2"
- }
- return loopbackTest(ctx, 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(ctx context.Context, ip net.IP, ipv6 bool) 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{ containerCase }
-
-var _ TestCase = (*NATPreRedirectIP)(nil)
-
-// Name implements TestCase.Name.
-func (*NATPreRedirectIP) Name() string {
- return "NATPreRedirectIP"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATPreRedirectIP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- addrs, err := localAddrs(ipv6)
- 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(ipv6, rules); err != nil {
- return err
- }
- return listenUDP(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATPreRedirectIP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, dropPort, ipv6)
-}
-
-// NATPreDontRedirectIP tests that iptables matching with "-d" does not match
-// packets it shouldn't.
-type NATPreDontRedirectIP struct{ containerCase }
-
-var _ TestCase = (*NATPreDontRedirectIP)(nil)
-
-// Name implements TestCase.Name.
-func (*NATPreDontRedirectIP) Name() string {
- return "NATPreDontRedirectIP"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATPreDontRedirectIP) ContainerAction(ctx context.Context, 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(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATPreDontRedirectIP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// NATPreRedirectInvert tests that iptables can match with "! -d".
-type NATPreRedirectInvert struct{ containerCase }
-
-var _ TestCase = (*NATPreRedirectInvert)(nil)
-
-// Name implements TestCase.Name.
-func (*NATPreRedirectInvert) Name() string {
- return "NATPreRedirectInvert"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATPreRedirectInvert) ContainerAction(ctx context.Context, 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(ctx, acceptPort, ipv6)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATPreRedirectInvert) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, dropPort, ipv6)
-}
-
-// NATRedirectRequiresProtocol tests that use of the --to-ports flag requires a
-// protocol to be specified with -p.
-type NATRedirectRequiresProtocol struct{ baseCase }
-
-var _ TestCase = (*NATRedirectRequiresProtocol)(nil)
-
-// Name implements TestCase.Name.
-func (*NATRedirectRequiresProtocol) Name() string {
- return "NATRedirectRequiresProtocol"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATRedirectRequiresProtocol) ContainerAction(ctx context.Context, 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(ctx context.Context, ip net.IP, ipv6 bool) error {
- // No-op.
- return nil
-}
-
-// NATOutRedirectTCPPort tests that connections are redirected on specified ports.
-type NATOutRedirectTCPPort struct{ baseCase }
-
-var _ TestCase = (*NATOutRedirectTCPPort)(nil)
-
-// Name implements TestCase.Name.
-func (*NATOutRedirectTCPPort) Name() string {
- return "NATOutRedirectTCPPort"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATOutRedirectTCPPort) ContainerAction(ctx context.Context, 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
- }
-
- localAddr := net.TCPAddr{
- IP: net.ParseIP(localIP(ipv6)),
- Port: acceptPort,
- }
-
- // Starts listening on port.
- lConn, err := net.ListenTCP("tcp", &localAddr)
- if err != nil {
- return err
- }
- defer lConn.Close()
-
- // Accept connections on port.
- if err := connectTCP(ctx, ip, dropPort, ipv6); err != nil {
- return err
- }
-
- conn, err := lConn.AcceptTCP()
- if err != nil {
- return err
- }
- conn.Close()
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATOutRedirectTCPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return nil
-}
-
-// NATLoopbackSkipsPrerouting tests that packets sent via loopback aren't
-// affected by PREROUTING rules.
-type NATLoopbackSkipsPrerouting struct{ baseCase }
-
-var _ TestCase = (*NATLoopbackSkipsPrerouting)(nil)
-
-// Name implements TestCase.Name.
-func (*NATLoopbackSkipsPrerouting) Name() string {
- return "NATLoopbackSkipsPrerouting"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATLoopbackSkipsPrerouting) ContainerAction(ctx context.Context, 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(ctx, dest, acceptPort, ipv6)
- }()
-
- if err := listenTCP(ctx, acceptPort, ipv6); err != nil {
- return err
- }
- return <-sendCh
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATLoopbackSkipsPrerouting) LocalAction(ctx context.Context, 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{ baseCase }
-
-var _ TestCase = (*NATPreOriginalDst)(nil)
-
-// Name implements TestCase.Name.
-func (*NATPreOriginalDst) Name() string {
- return "NATPreOriginalDst"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATPreOriginalDst) ContainerAction(ctx context.Context, 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(ctx, ipv6, addrs)
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATPreOriginalDst) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return connectTCP(ctx, ip, dropPort, ipv6)
-}
-
-// NATOutOriginalDst tests that SO_ORIGINAL_DST returns the pre-NAT destination
-// of OUTBOUND NATted packets.
-type NATOutOriginalDst struct{ baseCase }
-
-var _ TestCase = (*NATOutOriginalDst)(nil)
-
-// Name implements TestCase.Name.
-func (*NATOutOriginalDst) Name() string {
- return "NATOutOriginalDst"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATOutOriginalDst) ContainerAction(ctx context.Context, 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(ctx, ip, dropPort, ipv6)
- }()
-
- if err := listenForRedirectedConn(ctx, ipv6, []net.IP{ip}); err != nil {
- return err
- }
- return <-connCh
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATOutOriginalDst) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // No-op.
- return nil
-}
-
-func listenForRedirectedConn(ctx context.Context, ipv6 bool, originalDsts []net.IP) error {
- // The net package doesn't give guaranteed access to the connection's
- // underlying FD, and thus we cannot call getsockopt. We have to use
- // traditional syscalls.
-
- // Create the listening socket, bind, listen, and accept.
- family := unix.AF_INET
- if ipv6 {
- family = unix.AF_INET6
- }
- sockfd, err := unix.Socket(family, unix.SOCK_STREAM, 0)
- if err != nil {
- return err
- }
- defer unix.Close(sockfd)
-
- var bindAddr unix.Sockaddr
- if ipv6 {
- bindAddr = &unix.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 = &unix.SockaddrInet4{
- Port: acceptPort,
- Addr: [4]byte{0, 0, 0, 0}, // INADDR_ANY
- }
- }
- if err := unix.Bind(sockfd, bindAddr); err != nil {
- return err
- }
-
- if err := unix.Listen(sockfd, 1); err != nil {
- return err
- }
-
- // Block on accept() in another goroutine.
- connCh := make(chan int)
- errCh := make(chan error)
- go func() {
- for {
- connFD, _, err := unix.Accept(sockfd)
- if errors.Is(err, unix.EINTR) {
- continue
- }
- if err != nil {
- errCh <- err
- return
- }
- connCh <- connFD
- return
- }
- }()
-
- // Wait for accept() to return or for the context to finish.
- var connFD int
- select {
- case <-ctx.Done():
- return ctx.Err()
- case err := <-errCh:
- return err
- case connFD = <-connCh:
- }
- defer unix.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
- }
- return addrMatches6(got, originalDsts, dropPort)
- }
-
- got, err := originalDestination4(connFD)
- if err != nil {
- return err
- }
- return addrMatches4(got, originalDsts, dropPort)
-}
-
-// loopbackTests runs an iptables rule and ensures that packets sent to
-// dest:dropPort are received by localhost:acceptPort.
-func loopbackTest(ctx context.Context, ipv6 bool, dest net.IP, args ...string) error {
- if err := natTable(ipv6, args...); err != nil {
- return err
- }
- sendCh := make(chan error, 1)
- listenCh := make(chan error, 1)
- go func() {
- sendCh <- sendUDPLoop(ctx, dest, dropPort, ipv6)
- }()
- go func() {
- listenCh <- listenUDP(ctx, acceptPort, ipv6)
- }()
- select {
- case err := <-listenCh:
- return err
- case err := <-sendCh:
- return err
- }
-}
-
-// NATPreRECVORIGDSTADDR tests that IP{V6}_RECVORIGDSTADDR gets the post-NAT
-// address on the PREROUTING chain.
-type NATPreRECVORIGDSTADDR struct{ containerCase }
-
-var _ TestCase = (*NATPreRECVORIGDSTADDR)(nil)
-
-// Name implements TestCase.Name.
-func (*NATPreRECVORIGDSTADDR) Name() string {
- return "NATPreRECVORIGDSTADDR"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATPreRECVORIGDSTADDR) ContainerAction(ctx context.Context, 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
- }
-
- if err := recvWithRECVORIGDSTADDR(ctx, ipv6, nil, redirectPort); err != nil {
- return err
- }
-
- return nil
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATPreRECVORIGDSTADDR) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- return sendUDPLoop(ctx, ip, acceptPort, ipv6)
-}
-
-// NATOutRECVORIGDSTADDR tests that IP{V6}_RECVORIGDSTADDR gets the post-NAT
-// address on the OUTPUT chain.
-type NATOutRECVORIGDSTADDR struct{ containerCase }
-
-var _ TestCase = (*NATOutRECVORIGDSTADDR)(nil)
-
-// Name implements TestCase.Name.
-func (*NATOutRECVORIGDSTADDR) Name() string {
- return "NATOutRECVORIGDSTADDR"
-}
-
-// ContainerAction implements TestCase.ContainerAction.
-func (*NATOutRECVORIGDSTADDR) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- if err := natTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
- return err
- }
-
- sendCh := make(chan error)
- go func() {
- // Packets will be sent to a non-container IP and redirected
- // back to the container.
- sendCh <- sendUDPLoop(ctx, ip, acceptPort, ipv6)
- }()
-
- expectedIP := &net.IP{127, 0, 0, 1}
- if ipv6 {
- expectedIP = &net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
- }
- if err := recvWithRECVORIGDSTADDR(ctx, ipv6, expectedIP, redirectPort); err != nil {
- return err
- }
-
- select {
- case err := <-sendCh:
- return err
- default:
- return nil
- }
-}
-
-// LocalAction implements TestCase.LocalAction.
-func (*NATOutRECVORIGDSTADDR) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
- // No-op.
- return nil
-}
-
-func recvWithRECVORIGDSTADDR(ctx context.Context, ipv6 bool, expectedDst *net.IP, port uint16) error {
- // The net package doesn't give guaranteed access to a connection's
- // underlying FD, and thus we cannot call getsockopt. We have to use
- // traditional syscalls for IP_RECVORIGDSTADDR.
-
- // Create the listening socket.
- var (
- family = unix.AF_INET
- level = unix.SOL_IP
- option = unix.IP_RECVORIGDSTADDR
- bindAddr unix.Sockaddr = &unix.SockaddrInet4{
- Port: int(port),
- Addr: [4]byte{0, 0, 0, 0}, // INADDR_ANY
- }
- )
- if ipv6 {
- family = unix.AF_INET6
- level = unix.SOL_IPV6
- option = 74 // IPV6_RECVORIGDSTADDR, which is missing from the syscall package.
- bindAddr = &unix.SockaddrInet6{
- Port: int(port),
- Addr: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // in6addr_any
- }
- }
- sockfd, err := unix.Socket(family, unix.SOCK_DGRAM, 0)
- if err != nil {
- return fmt.Errorf("failed Socket(%d, %d, 0): %w", family, unix.SOCK_DGRAM, err)
- }
- defer unix.Close(sockfd)
-
- if err := unix.Bind(sockfd, bindAddr); err != nil {
- return fmt.Errorf("failed Bind(%d, %+v): %v", sockfd, bindAddr, err)
- }
-
- // Enable IP_RECVORIGDSTADDR.
- if err := unix.SetsockoptInt(sockfd, level, option, 1); err != nil {
- return fmt.Errorf("failed SetsockoptByte(%d, %d, %d, 1): %v", sockfd, level, option, err)
- }
-
- addrCh := make(chan interface{})
- errCh := make(chan error)
- go func() {
- var addr interface{}
- var err error
- if ipv6 {
- addr, err = recvOrigDstAddr6(sockfd)
- } else {
- addr, err = recvOrigDstAddr4(sockfd)
- }
- if err != nil {
- errCh <- err
- } else {
- addrCh <- addr
- }
- }()
-
- // Wait to receive a packet.
- var addr interface{}
- select {
- case <-ctx.Done():
- return ctx.Err()
- case err := <-errCh:
- return err
- case addr = <-addrCh:
- }
-
- // Get a list of local IPs to verify that the packet now appears to have
- // been sent to us.
- var localAddrs []net.IP
- if expectedDst != nil {
- localAddrs = []net.IP{*expectedDst}
- } else {
- localAddrs, err = getInterfaceAddrs(ipv6)
- if err != nil {
- return fmt.Errorf("failed to get local interfaces: %w", err)
- }
- }
-
- // Verify that the address has the post-NAT port and address.
- if ipv6 {
- return addrMatches6(addr.(unix.RawSockaddrInet6), localAddrs, redirectPort)
- }
- return addrMatches4(addr.(unix.RawSockaddrInet4), localAddrs, redirectPort)
-}
-
-func recvOrigDstAddr4(sockfd int) (unix.RawSockaddrInet4, error) {
- buf, err := recvOrigDstAddr(sockfd, unix.SOL_IP, unix.SizeofSockaddrInet4)
- if err != nil {
- return unix.RawSockaddrInet4{}, err
- }
- var addr unix.RawSockaddrInet4
- binary.Unmarshal(buf, usermem.ByteOrder, &addr)
- return addr, nil
-}
-
-func recvOrigDstAddr6(sockfd int) (unix.RawSockaddrInet6, error) {
- buf, err := recvOrigDstAddr(sockfd, unix.SOL_IP, unix.SizeofSockaddrInet6)
- if err != nil {
- return unix.RawSockaddrInet6{}, err
- }
- var addr unix.RawSockaddrInet6
- binary.Unmarshal(buf, usermem.ByteOrder, &addr)
- return addr, nil
-}
-
-func recvOrigDstAddr(sockfd int, level uintptr, addrSize int) ([]byte, error) {
- buf := make([]byte, 64)
- oob := make([]byte, unix.CmsgSpace(addrSize))
- for {
- _, oobn, _, _, err := unix.Recvmsg(
- sockfd,
- buf, // Message buffer.
- oob, // Out-of-band buffer.
- 0) // Flags.
- if errors.Is(err, unix.EINTR) {
- continue
- }
- if err != nil {
- return nil, fmt.Errorf("failed when calling Recvmsg: %w", err)
- }
- oob = oob[:oobn]
-
- // Parse out the control message.
- msgs, err := unix.ParseSocketControlMessage(oob)
- if err != nil {
- return nil, fmt.Errorf("failed to parse control message: %w", err)
- }
- return msgs[0].Data, nil
- }
-}
-
-func addrMatches4(got unix.RawSockaddrInet4, wantAddrs []net.IP, port uint16) error {
- for _, wantAddr := range wantAddrs {
- want := unix.RawSockaddrInet4{
- Family: unix.AF_INET,
- Port: htons(port),
- }
- copy(want.Addr[:], wantAddr.To4())
- if got == want {
- return nil
- }
- }
- return fmt.Errorf("got %+v, but wanted one of %+v (note: port numbers are in network byte order)", got, wantAddrs)
-}
-
-func addrMatches6(got unix.RawSockaddrInet6, wantAddrs []net.IP, port uint16) error {
- for _, wantAddr := range wantAddrs {
- want := unix.RawSockaddrInet6{
- Family: unix.AF_INET6,
- Port: htons(port),
- }
- copy(want.Addr[:], wantAddr.To16())
- if got == want {
- return nil
- }
- }
- return fmt.Errorf("got %+v, but wanted one of %+v (note: port numbers are in network byte order)", got, wantAddrs)
-}
diff --git a/test/iptables/runner/BUILD b/test/iptables/runner/BUILD
deleted file mode 100644
index 24504a1b9..000000000
--- a/test/iptables/runner/BUILD
+++ /dev/null
@@ -1,12 +0,0 @@
-load("//tools:defs.bzl", "go_binary")
-
-package(licenses = ["notice"])
-
-go_binary(
- name = "runner",
- testonly = 1,
- srcs = ["main.go"],
- pure = True,
- visibility = ["//test/iptables:__subpackages__"],
- deps = ["//test/iptables"],
-)
diff --git a/test/iptables/runner/main.go b/test/iptables/runner/main.go
deleted file mode 100644
index 9ae2d1b4d..000000000
--- a/test/iptables/runner/main.go
+++ /dev/null
@@ -1,79 +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 (
- "context"
- "flag"
- "fmt"
- "log"
- "net"
-
- "gvisor.dev/gvisor/test/iptables"
-)
-
-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()
-
- // 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.
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
- if err := test.ContainerAction(ctx, ip, *ipv6); err != nil {
- log.Fatalf("Failed running test %q: %v", *name, err)
- }
-
- // Emit the final line.
- log.Printf("%s", iptables.TerminalStatement)
-}
-
-// 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("tcp", &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
-}