mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Automated rollback of changelist 311424257
PiperOrigin-RevId: 313300554
This commit is contained in:
@@ -148,6 +148,62 @@ func (m MountMode) String() string {
|
||||
panic(fmt.Sprintf("invalid mode: %d", m))
|
||||
}
|
||||
|
||||
// DockerNetwork contains the name of a docker network.
|
||||
type DockerNetwork struct {
|
||||
logger testutil.Logger
|
||||
Name string
|
||||
Subnet *net.IPNet
|
||||
containers []*Docker
|
||||
}
|
||||
|
||||
// NewDockerNetwork sets up the struct for a Docker network. Names of networks
|
||||
// will be unique.
|
||||
func NewDockerNetwork(logger testutil.Logger) *DockerNetwork {
|
||||
return &DockerNetwork{
|
||||
logger: logger,
|
||||
Name: testutil.RandomID(logger.Name()),
|
||||
}
|
||||
}
|
||||
|
||||
// Create calls 'docker network create'.
|
||||
func (n *DockerNetwork) Create(args ...string) error {
|
||||
a := []string{"docker", "network", "create"}
|
||||
if n.Subnet != nil {
|
||||
a = append(a, fmt.Sprintf("--subnet=%s", n.Subnet))
|
||||
}
|
||||
a = append(a, args...)
|
||||
a = append(a, n.Name)
|
||||
return testutil.Command(n.logger, a...).Run()
|
||||
}
|
||||
|
||||
// Connect calls 'docker network connect' with the arguments provided.
|
||||
func (n *DockerNetwork) Connect(container *Docker, args ...string) error {
|
||||
a := []string{"docker", "network", "connect"}
|
||||
a = append(a, args...)
|
||||
a = append(a, n.Name, container.Name)
|
||||
if err := testutil.Command(n.logger, a...).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
n.containers = append(n.containers, container)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cleanup cleans up the docker network and all the containers attached to it.
|
||||
func (n *DockerNetwork) Cleanup() error {
|
||||
for _, c := range n.containers {
|
||||
// Don't propagate the error, it might be that the container
|
||||
// was already cleaned up.
|
||||
if err := c.Kill(); err != nil {
|
||||
n.logger.Logf("unable to kill container during cleanup: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := testutil.Command(n.logger, "docker", "network", "rm", n.Name).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Docker contains the name and the runtime of a docker container.
|
||||
type Docker struct {
|
||||
logger testutil.Logger
|
||||
@@ -313,7 +369,9 @@ func (d *Docker) argsFor(r *RunOpts, command string, p []string) (rv []string) {
|
||||
rv = append(rv, d.Name)
|
||||
} else {
|
||||
rv = append(rv, d.mounts...)
|
||||
rv = append(rv, fmt.Sprintf("--runtime=%s", d.Runtime))
|
||||
if len(d.Runtime) > 0 {
|
||||
rv = append(rv, fmt.Sprintf("--runtime=%s", d.Runtime))
|
||||
}
|
||||
rv = append(rv, fmt.Sprintf("--name=%s", d.Name))
|
||||
rv = append(rv, testutil.ImageByName(r.Image))
|
||||
}
|
||||
@@ -481,6 +539,56 @@ func (d *Docker) FindIP() (net.IP, error) {
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// A NetworkInterface is container's network interface information.
|
||||
type NetworkInterface struct {
|
||||
IPv4 net.IP
|
||||
MAC net.HardwareAddr
|
||||
}
|
||||
|
||||
// ListNetworks returns the network interfaces of the container, keyed by
|
||||
// Docker network name.
|
||||
func (d *Docker) ListNetworks() (map[string]NetworkInterface, error) {
|
||||
const format = `{{json .NetworkSettings.Networks}}`
|
||||
out, err := testutil.Command(d.logger, "docker", "inspect", "-f", format, d.Name).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error network interfaces: %q: %w", string(out), err)
|
||||
}
|
||||
|
||||
networks := map[string]map[string]string{}
|
||||
if err := json.Unmarshal(out, &networks); err != nil {
|
||||
return nil, fmt.Errorf("error decoding network interfaces: %w", err)
|
||||
}
|
||||
|
||||
interfaces := map[string]NetworkInterface{}
|
||||
for name, iface := range networks {
|
||||
var netface NetworkInterface
|
||||
|
||||
rawIP := strings.TrimSpace(iface["IPAddress"])
|
||||
if rawIP != "" {
|
||||
ip := net.ParseIP(rawIP)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("invalid IP: %q", rawIP)
|
||||
}
|
||||
// Docker's IPAddress field is IPv4. The IPv6 address
|
||||
// is stored in the GlobalIPv6Address field.
|
||||
netface.IPv4 = ip
|
||||
}
|
||||
|
||||
rawMAC := strings.TrimSpace(iface["MacAddress"])
|
||||
if rawMAC != "" {
|
||||
mac, err := net.ParseMAC(rawMAC)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid MAC: %q: %w", rawMAC, err)
|
||||
}
|
||||
netface.MAC = mac
|
||||
}
|
||||
|
||||
interfaces[name] = netface
|
||||
}
|
||||
|
||||
return interfaces, nil
|
||||
}
|
||||
|
||||
// SandboxPid returns the PID to the sandbox process.
|
||||
func (d *Docker) SandboxPid() (int, error) {
|
||||
out, err := testutil.Command(d.logger, "docker", "inspect", "-f={{.State.Pid}}", d.Name).CombinedOutput()
|
||||
|
||||
@@ -18,6 +18,27 @@ Packetimpact aims to provide:
|
||||
* **Control-flow** like for loops, conditionals, and variables.
|
||||
* **Flexibilty** to specify every byte in a packet or use multiple sockets.
|
||||
|
||||
## How to run packetimpact tests?
|
||||
|
||||
Build the test container image by running the following at the root of the
|
||||
repository:
|
||||
|
||||
```bash
|
||||
$ make load-packetimpact
|
||||
```
|
||||
|
||||
Run a test, e.g. `fin_wait2_timeout`, against Linux:
|
||||
|
||||
```bash
|
||||
$ bazel test //test/packetimpact/tests:fin_wait2_timeout_linux_test
|
||||
```
|
||||
|
||||
Run the same test, but against gVisor:
|
||||
|
||||
```bash
|
||||
$ bazel test //test/packetimpact/tests:fin_wait2_timeout_netstack_test
|
||||
```
|
||||
|
||||
## When to use packetimpact?
|
||||
|
||||
There are a few ways to write networking tests for gVisor currently:
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "netdevs",
|
||||
srcs = ["netdevs.go"],
|
||||
visibility = ["//test/packetimpact:__subpackages__"],
|
||||
deps = [
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/header",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,104 @@
|
||||
// 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 netdevs contains utilities for working with network devices.
|
||||
package netdevs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
// A DeviceInfo represents a network device.
|
||||
type DeviceInfo struct {
|
||||
MAC net.HardwareAddr
|
||||
IPv4Addr net.IP
|
||||
IPv4Net *net.IPNet
|
||||
IPv6Addr net.IP
|
||||
IPv6Net *net.IPNet
|
||||
}
|
||||
|
||||
var (
|
||||
deviceLine = regexp.MustCompile(`^\s*\d+: (\w+)`)
|
||||
linkLine = regexp.MustCompile(`^\s*link/\w+ ([0-9a-fA-F:]+)`)
|
||||
inetLine = regexp.MustCompile(`^\s*inet ([0-9./]+)`)
|
||||
inet6Line = regexp.MustCompile(`^\s*inet6 ([0-9a-fA-Z:/]+)`)
|
||||
)
|
||||
|
||||
// ParseDevices parses the output from `ip addr show` into a map from device
|
||||
// name to information about the device.
|
||||
func ParseDevices(cmdOutput string) (map[string]DeviceInfo, error) {
|
||||
var currentDevice string
|
||||
var currentInfo DeviceInfo
|
||||
deviceInfos := make(map[string]DeviceInfo)
|
||||
for _, line := range strings.Split(cmdOutput, "\n") {
|
||||
if m := deviceLine.FindStringSubmatch(line); m != nil {
|
||||
if currentDevice != "" {
|
||||
deviceInfos[currentDevice] = currentInfo
|
||||
}
|
||||
currentInfo = DeviceInfo{}
|
||||
currentDevice = m[1]
|
||||
} else if m := linkLine.FindStringSubmatch(line); m != nil {
|
||||
mac, err := net.ParseMAC(m[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentInfo.MAC = mac
|
||||
} else if m := inetLine.FindStringSubmatch(line); m != nil {
|
||||
ipv4Addr, ipv4Net, err := net.ParseCIDR(m[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentInfo.IPv4Addr = ipv4Addr
|
||||
currentInfo.IPv4Net = ipv4Net
|
||||
} else if m := inet6Line.FindStringSubmatch(line); m != nil {
|
||||
ipv6Addr, ipv6Net, err := net.ParseCIDR(m[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentInfo.IPv6Addr = ipv6Addr
|
||||
currentInfo.IPv6Net = ipv6Net
|
||||
}
|
||||
}
|
||||
if currentDevice != "" {
|
||||
deviceInfos[currentDevice] = currentInfo
|
||||
}
|
||||
return deviceInfos, nil
|
||||
}
|
||||
|
||||
// MACToIP converts the MAC address to an IPv6 link local address as described
|
||||
// in RFC 4291 page 20: https://tools.ietf.org/html/rfc4291#page-20
|
||||
func MACToIP(mac net.HardwareAddr) net.IP {
|
||||
addr := make([]byte, header.IPv6AddressSize)
|
||||
addr[0] = 0xfe
|
||||
addr[1] = 0x80
|
||||
header.EthernetAdddressToModifiedEUI64IntoBuf(tcpip.LinkAddress(mac), addr[8:])
|
||||
return net.IP(addr)
|
||||
}
|
||||
|
||||
// FindDeviceByIP finds a DeviceInfo and device name from an IP address in the
|
||||
// output of ParseDevices.
|
||||
func FindDeviceByIP(ip net.IP, devices map[string]DeviceInfo) (string, DeviceInfo, error) {
|
||||
for dev, info := range devices {
|
||||
if info.IPv4Addr.Equal(ip) {
|
||||
return dev, info, nil
|
||||
}
|
||||
}
|
||||
return "", DeviceInfo{}, fmt.Errorf("can't find %s on any interface", ip)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
load("//tools:defs.bzl", "go_test")
|
||||
|
||||
package(
|
||||
default_visibility = ["//test/packetimpact:__subpackages__"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "packetimpact_test",
|
||||
srcs = ["packetimpact_test.go"],
|
||||
tags = [
|
||||
# Not intended to be run directly.
|
||||
"local",
|
||||
"manual",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/test/dockerutil",
|
||||
"//test/packetimpact/netdevs",
|
||||
],
|
||||
)
|
||||
@@ -11,12 +11,10 @@ def _packetimpact_test_impl(ctx):
|
||||
# permission problems, because all runfiles may not be owned by the
|
||||
# current user, and no other users will be mapped in that namespace.
|
||||
# Make sure that everything is readable here.
|
||||
"find . -type f -exec chmod a+rx {} \\;",
|
||||
"find . -type d -exec chmod a+rx {} \\;",
|
||||
"%s %s --posix_server_binary %s --testbench_binary %s $@\n" % (
|
||||
"find . -type f -or -type d -exec chmod a+rx {} \\;",
|
||||
"%s %s --testbench_binary %s $@\n" % (
|
||||
test_runner.short_path,
|
||||
" ".join(ctx.attr.flags),
|
||||
ctx.files._posix_server_binary[0].short_path,
|
||||
ctx.files.testbench_binary[0].short_path,
|
||||
),
|
||||
])
|
||||
@@ -38,7 +36,7 @@ _packetimpact_test = rule(
|
||||
"_test_runner": attr.label(
|
||||
executable = True,
|
||||
cfg = "target",
|
||||
default = ":test_runner",
|
||||
default = ":packetimpact_test",
|
||||
),
|
||||
"_posix_server_binary": attr.label(
|
||||
cfg = "target",
|
||||
@@ -69,6 +67,7 @@ def packetimpact_linux_test(
|
||||
Args:
|
||||
name: name of the test
|
||||
testbench_binary: the testbench binary
|
||||
expect_failure: the test must fail
|
||||
**kwargs: all the other args, forwarded to _packetimpact_test
|
||||
"""
|
||||
expect_failure_flag = ["--expect_failure"] if expect_failure else []
|
||||
@@ -113,8 +112,8 @@ def packetimpact_go_test(name, size = "small", pure = True, expect_linux_failure
|
||||
name: name of the test
|
||||
size: size of the test
|
||||
pure: make a static go binary
|
||||
expect_linux_failure: expect the test to fail for Linux
|
||||
expect_netstack_failure: expect the test to fail for Netstack
|
||||
expect_linux_failure: the test must fail for Linux
|
||||
expect_netstack_failure: the test must fail for Netstack
|
||||
**kwargs: all the other args, forwarded to go_test
|
||||
"""
|
||||
testbench_binary = name + "_test"
|
||||
@@ -0,0 +1,312 @@
|
||||
// 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.
|
||||
|
||||
// The runner starts docker containers and networking for a packetimpact test.
|
||||
package packetimpact_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/test/dockerutil"
|
||||
"gvisor.dev/gvisor/test/packetimpact/netdevs"
|
||||
)
|
||||
|
||||
// stringList implements flag.Value.
|
||||
type stringList []string
|
||||
|
||||
// String implements flag.Value.String.
|
||||
func (l *stringList) String() string {
|
||||
return strings.Join(*l, ",")
|
||||
}
|
||||
|
||||
// Set implements flag.Value.Set.
|
||||
func (l *stringList) Set(value string) error {
|
||||
*l = append(*l, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
dutPlatform = flag.String("dut_platform", "", "either \"linux\" or \"netstack\"")
|
||||
testbenchBinary = flag.String("testbench_binary", "", "path to the testbench binary")
|
||||
tshark = flag.Bool("tshark", false, "use more verbose tshark in logs instead of tcpdump")
|
||||
extraTestArgs = stringList{}
|
||||
expectFailure = flag.Bool("expect_failure", false, "expect that the test will fail when run")
|
||||
|
||||
dutAddr = net.IPv4(0, 0, 0, 10)
|
||||
testbenchAddr = net.IPv4(0, 0, 0, 20)
|
||||
)
|
||||
|
||||
const ctrlPort = "40000"
|
||||
|
||||
// logger implements testutil.Logger.
|
||||
//
|
||||
// Labels logs based on their source and formats multi-line logs.
|
||||
type logger string
|
||||
|
||||
// Name implements testutil.Logger.Name.
|
||||
func (l logger) Name() string {
|
||||
return string(l)
|
||||
}
|
||||
|
||||
// Logf implements testutil.Logger.Logf.
|
||||
func (l logger) Logf(format string, args ...interface{}) {
|
||||
lines := strings.Split(fmt.Sprintf(format, args...), "\n")
|
||||
log.Printf("%s: %s", l, lines[0])
|
||||
for _, line := range lines[1:] {
|
||||
log.Printf("%*s %s", len(l), "", line)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOne(t *testing.T) {
|
||||
flag.Var(&extraTestArgs, "extra_test_arg", "extra arguments to pass to the testbench")
|
||||
flag.Parse()
|
||||
if *dutPlatform != "linux" && *dutPlatform != "netstack" {
|
||||
t.Fatal("--dut_platform should be either linux or netstack")
|
||||
}
|
||||
if *testbenchBinary == "" {
|
||||
t.Fatal("--testbench_binary is missing")
|
||||
}
|
||||
if *dutPlatform == "netstack" {
|
||||
if _, err := dockerutil.RuntimePath(); err != nil {
|
||||
t.Fatal("--runtime is missing or invalid with --dut_platform=netstack:", err)
|
||||
}
|
||||
}
|
||||
dockerutil.EnsureSupportedDockerVersion()
|
||||
|
||||
// Create the networks needed for the test. One control network is needed for
|
||||
// the gRPC control packets and one test network on which to transmit the test
|
||||
// packets.
|
||||
ctrlNet := dockerutil.NewDockerNetwork(logger("ctrlNet"))
|
||||
testNet := dockerutil.NewDockerNetwork(logger("testNet"))
|
||||
for _, dn := range []*dockerutil.DockerNetwork{ctrlNet, testNet} {
|
||||
for {
|
||||
if err := createDockerNetwork(dn); err != nil {
|
||||
t.Log("creating docker network:", err)
|
||||
const wait = 100 * time.Millisecond
|
||||
t.Logf("sleeping %s and will try creating docker network again", wait)
|
||||
// This can fail if another docker network claimed the same IP so we'll
|
||||
// just try again.
|
||||
time.Sleep(wait)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
defer func(dn *dockerutil.DockerNetwork) {
|
||||
if err := dn.Cleanup(); err != nil {
|
||||
t.Errorf("unable to cleanup container %s: %s", dn.Name, err)
|
||||
}
|
||||
}(dn)
|
||||
}
|
||||
|
||||
runOpts := dockerutil.RunOpts{
|
||||
Image: "packetimpact",
|
||||
CapAdd: []string{"NET_ADMIN"},
|
||||
Extra: []string{"--sysctl", "net.ipv6.conf.all.disable_ipv6=0", "--rm"},
|
||||
Foreground: true,
|
||||
}
|
||||
|
||||
// Create the Docker container for the DUT.
|
||||
dut := dockerutil.MakeDocker(logger("dut"))
|
||||
if *dutPlatform == "linux" {
|
||||
dut.Runtime = ""
|
||||
}
|
||||
|
||||
const containerPosixServerBinary = "/packetimpact/posix_server"
|
||||
dut.CopyFiles("/packetimpact", "/test/packetimpact/dut/posix_server")
|
||||
|
||||
if err := dut.Create(runOpts, containerPosixServerBinary, "--ip=0.0.0.0", "--port="+ctrlPort); err != nil {
|
||||
t.Fatalf("unable to create container %s: %s", dut.Name, err)
|
||||
}
|
||||
defer dut.CleanUp()
|
||||
|
||||
// Add ctrlNet as eth1 and testNet as eth2.
|
||||
const testNetDev = "eth2"
|
||||
if err := addNetworks(dut, dutAddr, []*dockerutil.DockerNetwork{ctrlNet, testNet}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := dut.Start(); err != nil {
|
||||
t.Fatalf("unable to start container %s: %s", dut.Name, err)
|
||||
}
|
||||
|
||||
if _, err := dut.WaitForOutput("Server listening.*\n", 60*time.Second); err != nil {
|
||||
t.Fatalf("%s on container %s never listened: %s", containerPosixServerBinary, dut.Name, err)
|
||||
}
|
||||
|
||||
dutTestDevice, dutDeviceInfo, err := deviceByIP(dut, addressInSubnet(dutAddr, *testNet.Subnet))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
remoteMAC := dutDeviceInfo.MAC
|
||||
remoteIPv6 := dutDeviceInfo.IPv6Addr
|
||||
// Netstack as DUT doesn't assign IPv6 addresses automatically so do it if
|
||||
// needed.
|
||||
if remoteIPv6 == nil {
|
||||
if _, err := dut.Exec(dockerutil.RunOpts{}, "ip", "addr", "add", netdevs.MACToIP(remoteMAC).String(), "scope", "link", "dev", dutTestDevice); err != nil {
|
||||
t.Fatalf("unable to ip addr add on container %s: %s", dut.Name, err)
|
||||
}
|
||||
// Now try again, to make sure that it worked.
|
||||
_, dutDeviceInfo, err = deviceByIP(dut, addressInSubnet(dutAddr, *testNet.Subnet))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
remoteIPv6 = dutDeviceInfo.IPv6Addr
|
||||
if remoteIPv6 == nil {
|
||||
t.Fatal("unable to set IPv6 address on container", dut.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the Docker container for the testbench.
|
||||
testbench := dockerutil.MakeDocker(logger("testbench"))
|
||||
testbench.Runtime = "" // The testbench always runs on Linux.
|
||||
|
||||
tbb := path.Base(*testbenchBinary)
|
||||
containerTestbenchBinary := "/packetimpact/" + tbb
|
||||
testbench.CopyFiles("/packetimpact", "/test/packetimpact/tests/"+tbb)
|
||||
|
||||
// Run tcpdump in the test bench unbuffered, without DNS resolution, just on
|
||||
// the interface with the test packets.
|
||||
snifferArgs := []string{
|
||||
"tcpdump", "-S", "-vvv", "-U", "-n", "-i", testNetDev,
|
||||
}
|
||||
snifferRegex := "tcpdump: listening.*\n"
|
||||
if *tshark {
|
||||
// Run tshark in the test bench unbuffered, without DNS resolution, just on
|
||||
// the interface with the test packets.
|
||||
snifferArgs = []string{
|
||||
"tshark", "-V", "-l", "-n", "-i", testNetDev,
|
||||
"-o", "tcp.check_checksum:TRUE",
|
||||
"-o", "udp.check_checksum:TRUE",
|
||||
}
|
||||
snifferRegex = "Capturing on.*\n"
|
||||
}
|
||||
|
||||
if err := testbench.Create(runOpts, snifferArgs...); err != nil {
|
||||
t.Fatalf("unable to create container %s: %s", testbench.Name, err)
|
||||
}
|
||||
defer testbench.CleanUp()
|
||||
|
||||
// Add ctrlNet as eth1 and testNet as eth2.
|
||||
if err := addNetworks(testbench, testbenchAddr, []*dockerutil.DockerNetwork{ctrlNet, testNet}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := testbench.Start(); err != nil {
|
||||
t.Fatalf("unable to start container %s: %s", testbench.Name, err)
|
||||
}
|
||||
|
||||
// Kill so that it will flush output.
|
||||
defer testbench.Exec(dockerutil.RunOpts{}, "killall", snifferArgs[0])
|
||||
|
||||
if _, err := testbench.WaitForOutput(snifferRegex, 60*time.Second); err != nil {
|
||||
t.Fatalf("sniffer on %s never listened: %s", dut.Name, err)
|
||||
}
|
||||
|
||||
// Because the Linux kernel receives the SYN-ACK but didn't send the SYN it
|
||||
// will issue a RST. To prevent this IPtables can be used to filter out all
|
||||
// incoming packets. The raw socket that packetimpact tests use will still see
|
||||
// everything.
|
||||
if _, err := testbench.Exec(dockerutil.RunOpts{}, "iptables", "-A", "INPUT", "-i", testNetDev, "-j", "DROP"); err != nil {
|
||||
t.Fatalf("unable to Exec iptables on container %s: %s", testbench.Name, err)
|
||||
}
|
||||
|
||||
// FIXME(b/156449515): Some piece of the system has a race. The old
|
||||
// bash script version had a sleep, so we have one too. The race should
|
||||
// be fixed and this sleep removed.
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// Start a packetimpact test on the test bench. The packetimpact test sends
|
||||
// and receives packets and also sends POSIX socket commands to the
|
||||
// posix_server to be executed on the DUT.
|
||||
testArgs := []string{containerTestbenchBinary}
|
||||
testArgs = append(testArgs, extraTestArgs...)
|
||||
testArgs = append(testArgs,
|
||||
"--posix_server_ip", addressInSubnet(dutAddr, *ctrlNet.Subnet).String(),
|
||||
"--posix_server_port", ctrlPort,
|
||||
"--remote_ipv4", addressInSubnet(dutAddr, *testNet.Subnet).String(),
|
||||
"--local_ipv4", addressInSubnet(testbenchAddr, *testNet.Subnet).String(),
|
||||
"--remote_ipv6", remoteIPv6.String(),
|
||||
"--remote_mac", remoteMAC.String(),
|
||||
"--device", testNetDev,
|
||||
)
|
||||
_, err = testbench.Exec(dockerutil.RunOpts{}, testArgs...)
|
||||
if !*expectFailure && err != nil {
|
||||
t.Fatal("test failed:", err)
|
||||
}
|
||||
if *expectFailure && err == nil {
|
||||
t.Fatal("test failure expected but the test succeeded, enable the test and mark the corresponding bug as fixed")
|
||||
}
|
||||
}
|
||||
|
||||
func addNetworks(d *dockerutil.Docker, addr net.IP, networks []*dockerutil.DockerNetwork) error {
|
||||
for _, dn := range networks {
|
||||
ip := addressInSubnet(addr, *dn.Subnet)
|
||||
// Connect to the network with the specified IP address.
|
||||
if err := dn.Connect(d, "--ip", ip.String()); err != nil {
|
||||
return fmt.Errorf("unable to connect container %s to network %s: %w", d.Name, dn.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addressInSubnet combines the subnet provided with the address and returns a
|
||||
// new address. The return address bits come from the subnet where the mask is 1
|
||||
// and from the ip address where the mask is 0.
|
||||
func addressInSubnet(addr net.IP, subnet net.IPNet) net.IP {
|
||||
var octets []byte
|
||||
for i := 0; i < 4; i++ {
|
||||
octets = append(octets, (subnet.IP.To4()[i]&subnet.Mask[i])+(addr.To4()[i]&(^subnet.Mask[i])))
|
||||
}
|
||||
return net.IP(octets)
|
||||
}
|
||||
|
||||
// makeDockerNetwork makes a randomly-named network that will start with the
|
||||
// namePrefix. The network will be a random /24 subnet.
|
||||
func createDockerNetwork(n *dockerutil.DockerNetwork) error {
|
||||
randSource := rand.NewSource(time.Now().UnixNano())
|
||||
r1 := rand.New(randSource)
|
||||
// Class C, 192.0.0.0 to 223.255.255.255, transitionally has mask 24.
|
||||
ip := net.IPv4(byte(r1.Intn(224-192)+192), byte(r1.Intn(256)), byte(r1.Intn(256)), 0)
|
||||
n.Subnet = &net.IPNet{
|
||||
IP: ip,
|
||||
Mask: ip.DefaultMask(),
|
||||
}
|
||||
return n.Create()
|
||||
}
|
||||
|
||||
// deviceByIP finds a deviceInfo and device name from an IP address.
|
||||
func deviceByIP(d *dockerutil.Docker, ip net.IP) (string, netdevs.DeviceInfo, error) {
|
||||
out, err := d.Exec(dockerutil.RunOpts{}, "ip", "addr", "show")
|
||||
if err != nil {
|
||||
return "", netdevs.DeviceInfo{}, fmt.Errorf("listing devices on %s container: %w", d.Name, err)
|
||||
}
|
||||
devs, err := netdevs.ParseDevices(out)
|
||||
if err != nil {
|
||||
return "", netdevs.DeviceInfo{}, fmt.Errorf("parsing devices from %s container: %w", d.Name, err)
|
||||
}
|
||||
testDevice, deviceInfo, err := netdevs.FindDeviceByIP(ip, devs)
|
||||
if err != nil {
|
||||
return "", netdevs.DeviceInfo{}, fmt.Errorf("can't find deviceInfo for container %s: %w", d.Name, err)
|
||||
}
|
||||
return testDevice, deviceInfo, nil
|
||||
}
|
||||
@@ -21,6 +21,7 @@ go_library(
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/seqnum",
|
||||
"//pkg/usermem",
|
||||
"//test/packetimpact/netdevs",
|
||||
"//test/packetimpact/proto:posix_server_go_proto",
|
||||
"@com_github_google_go-cmp//cmp:go_default_library",
|
||||
"@com_github_google_go-cmp//cmp/cmpopts:go_default_library",
|
||||
|
||||
@@ -114,12 +114,12 @@ var _ layerState = (*etherState)(nil)
|
||||
func newEtherState(out, in Ether) (*etherState, error) {
|
||||
lMAC, err := tcpip.ParseMACAddress(LocalMAC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("parsing local MAC: %q: %w", LocalMAC, err)
|
||||
}
|
||||
|
||||
rMAC, err := tcpip.ParseMACAddress(RemoteMAC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("parsing remote MAC: %q: %w", RemoteMAC, err)
|
||||
}
|
||||
s := etherState{
|
||||
out: Ether{SrcAddr: &lMAC, DstAddr: &rMAC},
|
||||
|
||||
@@ -16,6 +16,7 @@ package testbench
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"net"
|
||||
"strconv"
|
||||
"syscall"
|
||||
@@ -37,6 +38,11 @@ type DUT struct {
|
||||
|
||||
// NewDUT creates a new connection with the DUT over gRPC.
|
||||
func NewDUT(t *testing.T) DUT {
|
||||
flag.Parse()
|
||||
if err := genPseudoFlags(); err != nil {
|
||||
t.Fatal("generating psuedo flags:", err)
|
||||
}
|
||||
|
||||
posixServerAddress := POSIXServerIP + ":" + strconv.Itoa(POSIXServerPort)
|
||||
conn, err := grpc.Dial(posixServerAddress, grpc.WithInsecure(), grpc.WithKeepaliveParams(keepalive.ClientParameters{Timeout: RPCKeepalive}))
|
||||
if err != nil {
|
||||
|
||||
@@ -16,7 +16,6 @@ package testbench
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
@@ -41,7 +40,6 @@ func htons(x uint16) uint16 {
|
||||
|
||||
// NewSniffer creates a Sniffer connected to *device.
|
||||
func NewSniffer(t *testing.T) (Sniffer, error) {
|
||||
flag.Parse()
|
||||
snifferFd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, int(htons(unix.ETH_P_ALL)))
|
||||
if err != nil {
|
||||
return Sniffer{}, err
|
||||
@@ -136,7 +134,6 @@ type Injector struct {
|
||||
|
||||
// NewInjector creates a new injector on *device.
|
||||
func NewInjector(t *testing.T) (Injector, error) {
|
||||
flag.Parse()
|
||||
ifInfo, err := net.InterfaceByName(Device)
|
||||
if err != nil {
|
||||
return Injector{}, err
|
||||
|
||||
@@ -16,7 +16,12 @@ package testbench
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/test/packetimpact/netdevs"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -55,9 +60,31 @@ func RegisterFlags(fs *flag.FlagSet) {
|
||||
fs.DurationVar(&RPCKeepalive, "rpc_keepalive", RPCKeepalive, "gRPC keepalive")
|
||||
fs.StringVar(&LocalIPv4, "local_ipv4", LocalIPv4, "local IPv4 address for test packets")
|
||||
fs.StringVar(&RemoteIPv4, "remote_ipv4", RemoteIPv4, "remote IPv4 address for test packets")
|
||||
fs.StringVar(&LocalIPv6, "local_ipv6", LocalIPv6, "local IPv6 address for test packets")
|
||||
fs.StringVar(&RemoteIPv6, "remote_ipv6", RemoteIPv6, "remote IPv6 address for test packets")
|
||||
fs.StringVar(&LocalMAC, "local_mac", LocalMAC, "local mac address for test packets")
|
||||
fs.StringVar(&RemoteMAC, "remote_mac", RemoteMAC, "remote mac address for test packets")
|
||||
fs.StringVar(&Device, "device", Device, "local device for test packets")
|
||||
}
|
||||
|
||||
// genPseudoFlags populates flag-like global config based on real flags.
|
||||
//
|
||||
// genPseudoFlags must only be called after flag.Parse.
|
||||
func genPseudoFlags() error {
|
||||
out, err := exec.Command("ip", "addr", "show").CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("listing devices: %q: %w", string(out), err)
|
||||
}
|
||||
devs, err := netdevs.ParseDevices(string(out))
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing devices: %w", err)
|
||||
}
|
||||
|
||||
_, deviceInfo, err := netdevs.FindDeviceByIP(net.ParseIP(LocalIPv4), devs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't find deviceInfo: %w", err)
|
||||
}
|
||||
|
||||
LocalMAC = deviceInfo.MAC.String()
|
||||
LocalIPv6 = deviceInfo.IPv6Addr.String()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
load("defs.bzl", "packetimpact_go_test")
|
||||
load("//test/packetimpact/runner:defs.bzl", "packetimpact_go_test")
|
||||
|
||||
package(
|
||||
default_visibility = ["//test/packetimpact:__subpackages__"],
|
||||
@@ -177,8 +177,3 @@ packetimpact_go_test(
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
sh_binary(
|
||||
name = "test_runner",
|
||||
srcs = ["test_runner.sh"],
|
||||
)
|
||||
|
||||
@@ -1,325 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 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.
|
||||
|
||||
# Run a packetimpact test. Two docker containers are made, one for the
|
||||
# Device-Under-Test (DUT) and one for the test bench. Each is attached with
|
||||
# two networks, one for control packets that aid the test and one for test
|
||||
# packets which are sent as part of the test and observed for correctness.
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
function failure() {
|
||||
local lineno=$1
|
||||
local msg=$2
|
||||
local filename="$0"
|
||||
echo "FAIL: $filename:$lineno: $msg"
|
||||
}
|
||||
trap 'failure ${LINENO} "$BASH_COMMAND"' ERR
|
||||
|
||||
declare -r LONGOPTS="dut_platform:,posix_server_binary:,testbench_binary:,runtime:,tshark,extra_test_arg:,expect_failure"
|
||||
|
||||
# Don't use declare below so that the error from getopt will end the script.
|
||||
PARSED=$(getopt --options "" --longoptions=$LONGOPTS --name "$0" -- "$@")
|
||||
|
||||
eval set -- "$PARSED"
|
||||
|
||||
declare -a EXTRA_TEST_ARGS
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
--dut_platform)
|
||||
# Either "linux" or "netstack".
|
||||
declare -r DUT_PLATFORM="$2"
|
||||
shift 2
|
||||
;;
|
||||
--posix_server_binary)
|
||||
declare -r POSIX_SERVER_BINARY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--testbench_binary)
|
||||
declare -r TESTBENCH_BINARY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--runtime)
|
||||
# Not readonly because there might be multiple --runtime arguments and we
|
||||
# want to use just the last one. Only used if --dut_platform is
|
||||
# "netstack".
|
||||
declare RUNTIME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--tshark)
|
||||
declare -r TSHARK="1"
|
||||
shift 1
|
||||
;;
|
||||
--extra_test_arg)
|
||||
EXTRA_TEST_ARGS+="$2"
|
||||
shift 2
|
||||
;;
|
||||
--expect_failure)
|
||||
declare -r EXPECT_FAILURE="1"
|
||||
shift 1
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "Programming error"
|
||||
exit 3
|
||||
esac
|
||||
done
|
||||
|
||||
# All the other arguments are scripts.
|
||||
declare -r scripts="$@"
|
||||
|
||||
# Check that the required flags are defined in a way that is safe for "set -u".
|
||||
if [[ "${DUT_PLATFORM-}" == "netstack" ]]; then
|
||||
if [[ -z "${RUNTIME-}" ]]; then
|
||||
echo "FAIL: Missing --runtime argument: ${RUNTIME-}"
|
||||
exit 2
|
||||
fi
|
||||
declare -r RUNTIME_ARG="--runtime ${RUNTIME}"
|
||||
elif [[ "${DUT_PLATFORM-}" == "linux" ]]; then
|
||||
declare -r RUNTIME_ARG=""
|
||||
else
|
||||
echo "FAIL: Bad or missing --dut_platform argument: ${DUT_PLATFORM-}"
|
||||
exit 2
|
||||
fi
|
||||
if [[ ! -f "${POSIX_SERVER_BINARY-}" ]]; then
|
||||
echo "FAIL: Bad or missing --posix_server_binary: ${POSIX_SERVER-}"
|
||||
exit 2
|
||||
fi
|
||||
if [[ ! -f "${TESTBENCH_BINARY-}" ]]; then
|
||||
echo "FAIL: Bad or missing --testbench_binary: ${TESTBENCH_BINARY-}"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
function new_net_prefix() {
|
||||
# Class C, 192.0.0.0 to 223.255.255.255, transitionally has mask 24.
|
||||
echo "$(shuf -i 192-223 -n 1).$(shuf -i 0-255 -n 1).$(shuf -i 0-255 -n 1)"
|
||||
}
|
||||
|
||||
# Variables specific to the control network and interface start with CTRL_.
|
||||
# Variables specific to the test network and interface start with TEST_.
|
||||
# Variables specific to the DUT start with DUT_.
|
||||
# Variables specific to the test bench start with TESTBENCH_.
|
||||
# Use random numbers so that test networks don't collide.
|
||||
declare CTRL_NET="ctrl_net-${RANDOM}${RANDOM}"
|
||||
declare CTRL_NET_PREFIX=$(new_net_prefix)
|
||||
declare TEST_NET="test_net-${RANDOM}${RANDOM}"
|
||||
declare TEST_NET_PREFIX=$(new_net_prefix)
|
||||
# On both DUT and test bench, testing packets are on the eth2 interface.
|
||||
declare -r TEST_DEVICE="eth2"
|
||||
# Number of bits in the *_NET_PREFIX variables.
|
||||
declare -r NET_MASK="24"
|
||||
# Last bits of the DUT's IP address.
|
||||
declare -r DUT_NET_SUFFIX=".10"
|
||||
# Control port.
|
||||
declare -r CTRL_PORT="40000"
|
||||
# Last bits of the test bench's IP address.
|
||||
declare -r TESTBENCH_NET_SUFFIX=".20"
|
||||
declare -r TIMEOUT="60"
|
||||
declare -r IMAGE_TAG="gcr.io/gvisor-presubmit/packetimpact"
|
||||
|
||||
# Make sure that docker is installed.
|
||||
docker --version
|
||||
|
||||
function finish {
|
||||
local cleanup_success=1
|
||||
|
||||
if [[ -z "${TSHARK-}" ]]; then
|
||||
# Kill tcpdump so that it will flush output.
|
||||
docker exec -t "${TESTBENCH}" \
|
||||
killall tcpdump || \
|
||||
cleanup_success=0
|
||||
else
|
||||
# Kill tshark so that it will flush output.
|
||||
docker exec -t "${TESTBENCH}" \
|
||||
killall tshark || \
|
||||
cleanup_success=0
|
||||
fi
|
||||
|
||||
for net in "${CTRL_NET}" "${TEST_NET}"; do
|
||||
# Kill all processes attached to ${net}.
|
||||
for docker_command in "kill" "rm"; do
|
||||
(docker network inspect "${net}" \
|
||||
--format '{{range $key, $value := .Containers}}{{$key}} {{end}}' \
|
||||
| xargs -r docker "${docker_command}") || \
|
||||
cleanup_success=0
|
||||
done
|
||||
# Remove the network.
|
||||
docker network rm "${net}" || \
|
||||
cleanup_success=0
|
||||
done
|
||||
|
||||
if ((!$cleanup_success)); then
|
||||
echo "FAIL: Cleanup command failed"
|
||||
exit 4
|
||||
fi
|
||||
}
|
||||
trap finish EXIT
|
||||
|
||||
# Subnet for control packets between test bench and DUT.
|
||||
while ! docker network create \
|
||||
"--subnet=${CTRL_NET_PREFIX}.0/${NET_MASK}" "${CTRL_NET}"; do
|
||||
sleep 0.1
|
||||
CTRL_NET_PREFIX=$(new_net_prefix)
|
||||
CTRL_NET="ctrl_net-${RANDOM}${RANDOM}"
|
||||
done
|
||||
|
||||
# Subnet for the packets that are part of the test.
|
||||
while ! docker network create \
|
||||
"--subnet=${TEST_NET_PREFIX}.0/${NET_MASK}" "${TEST_NET}"; do
|
||||
sleep 0.1
|
||||
TEST_NET_PREFIX=$(new_net_prefix)
|
||||
TEST_NET="test_net-${RANDOM}${RANDOM}"
|
||||
done
|
||||
|
||||
docker pull "${IMAGE_TAG}"
|
||||
|
||||
# Create the DUT container and connect to network.
|
||||
DUT=$(docker create ${RUNTIME_ARG} --privileged --rm \
|
||||
--cap-add NET_ADMIN \
|
||||
--sysctl net.ipv6.conf.all.disable_ipv6=0 \
|
||||
--stop-timeout ${TIMEOUT} -it ${IMAGE_TAG})
|
||||
docker network connect "${CTRL_NET}" \
|
||||
--ip "${CTRL_NET_PREFIX}${DUT_NET_SUFFIX}" "${DUT}" \
|
||||
|| (docker kill ${DUT}; docker rm ${DUT}; false)
|
||||
docker network connect "${TEST_NET}" \
|
||||
--ip "${TEST_NET_PREFIX}${DUT_NET_SUFFIX}" "${DUT}" \
|
||||
|| (docker kill ${DUT}; docker rm ${DUT}; false)
|
||||
docker start "${DUT}"
|
||||
|
||||
# Create the test bench container and connect to network.
|
||||
TESTBENCH=$(docker create --privileged --rm \
|
||||
--cap-add NET_ADMIN \
|
||||
--sysctl net.ipv6.conf.all.disable_ipv6=0 \
|
||||
--stop-timeout ${TIMEOUT} -it ${IMAGE_TAG})
|
||||
docker network connect "${CTRL_NET}" \
|
||||
--ip "${CTRL_NET_PREFIX}${TESTBENCH_NET_SUFFIX}" "${TESTBENCH}" \
|
||||
|| (docker kill ${TESTBENCH}; docker rm ${TESTBENCH}; false)
|
||||
docker network connect "${TEST_NET}" \
|
||||
--ip "${TEST_NET_PREFIX}${TESTBENCH_NET_SUFFIX}" "${TESTBENCH}" \
|
||||
|| (docker kill ${TESTBENCH}; docker rm ${TESTBENCH}; false)
|
||||
docker start "${TESTBENCH}"
|
||||
|
||||
# Start the posix_server in the DUT.
|
||||
declare -r DOCKER_POSIX_SERVER_BINARY="/$(basename ${POSIX_SERVER_BINARY})"
|
||||
docker cp -L ${POSIX_SERVER_BINARY} "${DUT}:${DOCKER_POSIX_SERVER_BINARY}"
|
||||
|
||||
docker exec -t "${DUT}" \
|
||||
/bin/bash -c "${DOCKER_POSIX_SERVER_BINARY} \
|
||||
--ip ${CTRL_NET_PREFIX}${DUT_NET_SUFFIX} \
|
||||
--port ${CTRL_PORT}" &
|
||||
|
||||
# Because the Linux kernel receives the SYN-ACK but didn't send the SYN it will
|
||||
# issue a RST. To prevent this IPtables can be used to filter those out.
|
||||
docker exec "${TESTBENCH}" \
|
||||
iptables -A INPUT -i ${TEST_DEVICE} -j DROP
|
||||
|
||||
# Wait for the DUT server to come up. Attempt to connect to it from the test
|
||||
# bench every 100 milliseconds until success.
|
||||
while ! docker exec "${TESTBENCH}" \
|
||||
nc -zv "${CTRL_NET_PREFIX}${DUT_NET_SUFFIX}" "${CTRL_PORT}"; do
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
declare -r REMOTE_MAC=$(docker exec -t "${DUT}" ip link show \
|
||||
"${TEST_DEVICE}" | tail -1 | cut -d' ' -f6)
|
||||
declare -r LOCAL_MAC=$(docker exec -t "${TESTBENCH}" ip link show \
|
||||
"${TEST_DEVICE}" | tail -1 | cut -d' ' -f6)
|
||||
declare REMOTE_IPV6=$(docker exec -t "${DUT}" ip addr show scope link \
|
||||
"${TEST_DEVICE}" | grep inet6 | cut -d' ' -f6 | cut -d'/' -f1)
|
||||
declare -r LOCAL_IPV6=$(docker exec -t "${TESTBENCH}" ip addr show scope link \
|
||||
"${TEST_DEVICE}" | grep inet6 | cut -d' ' -f6 | cut -d'/' -f1)
|
||||
|
||||
# Netstack as DUT doesn't assign IPv6 addresses automatically so do it if
|
||||
# needed. Convert the MAC address to an IPv6 link local address as described in
|
||||
# RFC 4291 page 20: https://tools.ietf.org/html/rfc4291#page-20
|
||||
if [[ -z "${REMOTE_IPV6}" ]]; then
|
||||
# Split the octets of the MAC into an array of strings.
|
||||
IFS=":" read -a REMOTE_OCTETS <<< "${REMOTE_MAC}"
|
||||
# Flip the global bit.
|
||||
REMOTE_OCTETS[0]=$(printf '%x' "$((0x${REMOTE_OCTETS[0]} ^ 2))")
|
||||
# Add the IPv6 address.
|
||||
docker exec "${DUT}" \
|
||||
ip addr add $(printf 'fe80::%02x%02x:%02xff:fe%02x:%02x%02x/64' \
|
||||
"0x${REMOTE_OCTETS[0]}" "0x${REMOTE_OCTETS[1]}" "0x${REMOTE_OCTETS[2]}" \
|
||||
"0x${REMOTE_OCTETS[3]}" "0x${REMOTE_OCTETS[4]}" "0x${REMOTE_OCTETS[5]}") \
|
||||
scope link \
|
||||
dev "${TEST_DEVICE}"
|
||||
# Re-extract the IPv6 address.
|
||||
# TODO(eyalsoha): Add "scope link" below when netstack supports correctly
|
||||
# creating link-local IPv6 addresses.
|
||||
REMOTE_IPV6=$(docker exec -t "${DUT}" ip addr show \
|
||||
"${TEST_DEVICE}" | grep inet6 | cut -d' ' -f6 | cut -d'/' -f1)
|
||||
fi
|
||||
|
||||
declare -r DOCKER_TESTBENCH_BINARY="/$(basename ${TESTBENCH_BINARY})"
|
||||
docker cp -L "${TESTBENCH_BINARY}" "${TESTBENCH}:${DOCKER_TESTBENCH_BINARY}"
|
||||
|
||||
if [[ -z "${TSHARK-}" ]]; then
|
||||
# Run tcpdump in the test bench unbuffered, without dns resolution, just on
|
||||
# the interface with the test packets.
|
||||
docker exec -t "${TESTBENCH}" \
|
||||
tcpdump -S -vvv -U -n -i "${TEST_DEVICE}" \
|
||||
net "${TEST_NET_PREFIX}/24" or \
|
||||
host "${REMOTE_IPV6}" or \
|
||||
host "${LOCAL_IPV6}" &
|
||||
else
|
||||
# Run tshark in the test bench unbuffered, without dns resolution, just on the
|
||||
# interface with the test packets.
|
||||
docker exec -t "${TESTBENCH}" \
|
||||
tshark -V -l -n -i "${TEST_DEVICE}" \
|
||||
-o tcp.check_checksum:TRUE \
|
||||
-o udp.check_checksum:TRUE \
|
||||
net "${TEST_NET_PREFIX}/24" or \
|
||||
host "${REMOTE_IPV6}" or \
|
||||
host "${LOCAL_IPV6}" &
|
||||
fi
|
||||
|
||||
# tcpdump and tshark take time to startup
|
||||
sleep 3
|
||||
|
||||
# Start a packetimpact test on the test bench. The packetimpact test sends and
|
||||
# receives packets and also sends POSIX socket commands to the posix_server to
|
||||
# be executed on the DUT.
|
||||
docker exec \
|
||||
-e XML_OUTPUT_FILE="/test.xml" \
|
||||
-e TEST_TARGET \
|
||||
-t "${TESTBENCH}" \
|
||||
/bin/bash -c "${DOCKER_TESTBENCH_BINARY} \
|
||||
${EXTRA_TEST_ARGS[@]-} \
|
||||
--posix_server_ip=${CTRL_NET_PREFIX}${DUT_NET_SUFFIX} \
|
||||
--posix_server_port=${CTRL_PORT} \
|
||||
--remote_ipv4=${TEST_NET_PREFIX}${DUT_NET_SUFFIX} \
|
||||
--local_ipv4=${TEST_NET_PREFIX}${TESTBENCH_NET_SUFFIX} \
|
||||
--remote_ipv6=${REMOTE_IPV6} \
|
||||
--local_ipv6=${LOCAL_IPV6} \
|
||||
--remote_mac=${REMOTE_MAC} \
|
||||
--local_mac=${LOCAL_MAC} \
|
||||
--device=${TEST_DEVICE}" && true
|
||||
declare -r TEST_RESULT="${?}"
|
||||
if [[ -z "${EXPECT_FAILURE-}" && "${TEST_RESULT}" != 0 ]]; then
|
||||
echo 'FAIL: This test was expected to pass.'
|
||||
exit ${TEST_RESULT}
|
||||
fi
|
||||
if [[ ! -z "${EXPECT_FAILURE-}" && "${TEST_RESULT}" == 0 ]]; then
|
||||
echo 'FAIL: This test was expected to fail but passed. Enable the test and' \
|
||||
'mark the corresponding bug as fixed.'
|
||||
exit 1
|
||||
fi
|
||||
echo PASS: No errors.
|
||||
Reference in New Issue
Block a user