summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/benchmarks/fs/BUILD1
-rw-r--r--test/benchmarks/fs/bazel_test.go61
-rw-r--r--test/benchmarks/fs/fio_test.go59
-rw-r--r--test/benchmarks/harness/BUILD1
-rw-r--r--test/benchmarks/harness/util.go52
-rw-r--r--test/benchmarks/tcp/tcp_proxy.go17
-rw-r--r--test/fuse/linux/mount_test.cc5
-rw-r--r--test/iptables/BUILD1
-rw-r--r--test/iptables/iptables_unsafe.go31
-rw-r--r--test/iptables/nat.go90
-rw-r--r--test/packetimpact/dut/posix_server.cc3
-rw-r--r--test/packetimpact/proto/posix_server.proto3
-rw-r--r--test/packetimpact/runner/defs.bzl15
-rw-r--r--test/packetimpact/runner/dut.go81
-rw-r--r--test/packetimpact/testbench/BUILD2
-rw-r--r--test/packetimpact/testbench/connections.go33
-rw-r--r--test/packetimpact/testbench/dut.go56
-rw-r--r--test/packetimpact/testbench/layers.go12
-rw-r--r--test/packetimpact/testbench/layers_test.go8
-rw-r--r--test/packetimpact/testbench/testbench.go62
-rw-r--r--test/packetimpact/tests/BUILD32
-rw-r--r--test/packetimpact/tests/fin_wait2_timeout_test.go10
-rw-r--r--test/packetimpact/tests/ipv4_id_uniqueness_test.go2
-rw-r--r--test/packetimpact/tests/tcp_cork_mss_test.go12
-rw-r--r--test/packetimpact/tests/tcp_fin_retransmission_test.go87
-rw-r--r--test/packetimpact/tests/tcp_handshake_window_size_test.go8
-rw-r--r--test/packetimpact/tests/tcp_info_test.go2
-rw-r--r--test/packetimpact/tests/tcp_linger_test.go31
-rw-r--r--test/packetimpact/tests/tcp_network_unreachable_test.go11
-rw-r--r--test/packetimpact/tests/tcp_noaccept_close_rst_test.go2
-rw-r--r--test/packetimpact/tests/tcp_outside_the_window_closing_test.go86
-rw-r--r--test/packetimpact/tests/tcp_outside_the_window_test.go12
-rw-r--r--test/packetimpact/tests/tcp_paws_mechanism_test.go14
-rw-r--r--test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go53
-rw-r--r--test/packetimpact/tests/tcp_rack_test.go20
-rw-r--r--test/packetimpact/tests/tcp_rcv_buf_space_test.go5
-rw-r--r--test/packetimpact/tests/tcp_retransmits_test.go36
-rw-r--r--test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go4
-rw-r--r--test/packetimpact/tests/tcp_synrcvd_reset_test.go10
-rw-r--r--test/packetimpact/tests/tcp_synsent_reset_test.go18
-rw-r--r--test/packetimpact/tests/tcp_timewait_reset_test.go14
-rw-r--r--test/packetimpact/tests/tcp_unacc_seq_ack_closing_test.go94
-rw-r--r--test/packetimpact/tests/tcp_unacc_seq_ack_test.go88
-rw-r--r--test/packetimpact/tests/tcp_user_timeout_test.go6
-rw-r--r--test/packetimpact/tests/tcp_window_shrink_test.go4
-rw-r--r--test/packetimpact/tests/tcp_zero_receive_window_test.go8
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go22
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_test.go12
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go12
-rw-r--r--test/packetimpact/tests/udp_discard_mcast_source_addr_test.go5
-rw-r--r--test/packetimpact/tests/udp_icmp_error_propagation_test.go21
-rw-r--r--test/packetimpact/tests/udp_send_recv_dgram_test.go3
-rw-r--r--test/perf/BUILD2
-rw-r--r--test/runner/gtest/gtest.go50
-rw-r--r--test/runner/runner.go226
-rw-r--r--test/runtimes/proctor/BUILD5
-rw-r--r--test/runtimes/proctor/lib/BUILD1
-rw-r--r--test/runtimes/proctor/lib/lib.go7
-rw-r--r--test/runtimes/proctor/main.go8
-rw-r--r--test/syscalls/BUILD6
-rw-r--r--test/syscalls/linux/BUILD6
-rw-r--r--test/syscalls/linux/proc.cc23
-rw-r--r--test/syscalls/linux/proc_net.cc37
-rw-r--r--test/syscalls/linux/pty.cc7
-rw-r--r--test/syscalls/linux/socket_generic_stress.cc136
-rw-r--r--test/syscalls/linux/socket_inet_loopback.cc6
-rw-r--r--test/uds/BUILD1
-rw-r--r--test/uds/uds.go24
68 files changed, 1219 insertions, 663 deletions
diff --git a/test/benchmarks/fs/BUILD b/test/benchmarks/fs/BUILD
index b4f967441..c94caab60 100644
--- a/test/benchmarks/fs/BUILD
+++ b/test/benchmarks/fs/BUILD
@@ -11,6 +11,7 @@ benchmark_test(
"//pkg/test/dockerutil",
"//test/benchmarks/harness",
"//test/benchmarks/tools",
+ "@com_github_docker_docker//api/types/mount:go_default_library",
],
)
diff --git a/test/benchmarks/fs/bazel_test.go b/test/benchmarks/fs/bazel_test.go
index 8baeff0db..7ced963f6 100644
--- a/test/benchmarks/fs/bazel_test.go
+++ b/test/benchmarks/fs/bazel_test.go
@@ -25,6 +25,13 @@ import (
"gvisor.dev/gvisor/test/benchmarks/tools"
)
+// Dimensions here are clean/dirty cache (do or don't drop caches)
+// and if the mount on which we are compiling is a tmpfs/bind mount.
+type benchmark struct {
+ clearCache bool // clearCache drops caches before running.
+ fstype string // type of filesystem to use.
+}
+
// Note: CleanCache versions of this test require running with root permissions.
func BenchmarkBuildABSL(b *testing.B) {
runBuildBenchmark(b, "benchmarks/absl", "/abseil-cpp", "absl/base/...")
@@ -45,17 +52,18 @@ func runBuildBenchmark(b *testing.B, image, workdir, target string) {
}
defer machine.CleanUp()
- // Dimensions here are clean/dirty cache (do or don't drop caches)
- // and if the mount on which we are compiling is a tmpfs/bind mount.
- benchmarks := []struct {
- clearCache bool // clearCache drops caches before running.
- tmpfs bool // tmpfs will run compilation on a tmpfs.
- }{
- {clearCache: true, tmpfs: false},
- {clearCache: false, tmpfs: false},
- {clearCache: true, tmpfs: true},
- {clearCache: false, tmpfs: true},
+ benchmarks := make([]benchmark, 0, 6)
+ for _, filesys := range []string{harness.BindFS, harness.TmpFS, harness.RootFS} {
+ benchmarks = append(benchmarks, benchmark{
+ clearCache: true,
+ fstype: filesys,
+ })
+ benchmarks = append(benchmarks, benchmark{
+ clearCache: false,
+ fstype: filesys,
+ })
}
+
for _, bm := range benchmarks {
pageCache := tools.Parameter{
Name: "page_cache",
@@ -67,10 +75,7 @@ func runBuildBenchmark(b *testing.B, image, workdir, target string) {
filesystem := tools.Parameter{
Name: "filesystem",
- Value: "bind",
- }
- if bm.tmpfs {
- filesystem.Value = "tmpfs"
+ Value: bm.fstype,
}
name, err := tools.ParametersToName(pageCache, filesystem)
if err != nil {
@@ -83,21 +88,25 @@ func runBuildBenchmark(b *testing.B, image, workdir, target string) {
container := machine.GetContainer(ctx, b)
defer container.CleanUp(ctx)
+ mts, prefix, cleanup, err := harness.MakeMount(machine, bm.fstype)
+ if err != nil {
+ b.Fatalf("Failed to make mount: %v", err)
+ }
+ defer cleanup()
+
+ runOpts := dockerutil.RunOpts{
+ Image: image,
+ Mounts: mts,
+ }
+
// Start a container and sleep.
- if err := container.Spawn(ctx, dockerutil.RunOpts{
- Image: image,
- }, "sleep", fmt.Sprintf("%d", 1000000)); err != nil {
+ if err := container.Spawn(ctx, runOpts, "sleep", fmt.Sprintf("%d", 1000000)); err != nil {
b.Fatalf("run failed with: %v", err)
}
- // If we are running on a tmpfs, copy to /tmp which is a tmpfs.
- prefix := ""
- if bm.tmpfs {
- if out, err := container.Exec(ctx, dockerutil.ExecOpts{},
- "cp", "-r", workdir, "/tmp/."); err != nil {
- b.Fatalf("failed to copy directory: %v (%s)", err, out)
- }
- prefix = "/tmp"
+ if out, err := container.Exec(ctx, dockerutil.ExecOpts{},
+ "cp", "-rf", workdir, prefix+"/."); err != nil {
+ b.Fatalf("failed to copy directory: %v (%s)", err, out)
}
b.ResetTimer()
@@ -118,7 +127,7 @@ func runBuildBenchmark(b *testing.B, image, workdir, target string) {
WorkDir: prefix + workdir,
}, "bazel", "build", "-c", "opt", target)
if err != nil {
- b.Fatalf("build failed with: %v", err)
+ b.Fatalf("build failed with: %v logs: %s", err, got)
}
b.StopTimer()
diff --git a/test/benchmarks/fs/fio_test.go b/test/benchmarks/fs/fio_test.go
index cc2d1cbbc..f783a2b33 100644
--- a/test/benchmarks/fs/fio_test.go
+++ b/test/benchmarks/fs/fio_test.go
@@ -21,7 +21,6 @@ import (
"strings"
"testing"
- "github.com/docker/docker/api/types/mount"
"gvisor.dev/gvisor/pkg/test/dockerutil"
"gvisor.dev/gvisor/test/benchmarks/harness"
"gvisor.dev/gvisor/test/benchmarks/tools"
@@ -70,7 +69,7 @@ func BenchmarkFio(b *testing.B) {
}
defer machine.CleanUp()
- for _, fsType := range []mount.Type{mount.TypeBind, mount.TypeTmpfs} {
+ for _, fsType := range []string{harness.BindFS, harness.TmpFS, harness.RootFS} {
for _, tc := range testCases {
operation := tools.Parameter{
Name: "operation",
@@ -82,7 +81,7 @@ func BenchmarkFio(b *testing.B) {
}
filesystem := tools.Parameter{
Name: "filesystem",
- Value: string(fsType),
+ Value: fsType,
}
name, err := tools.ParametersToName(operation, blockSize, filesystem)
if err != nil {
@@ -95,13 +94,7 @@ func BenchmarkFio(b *testing.B) {
container := machine.GetContainer(ctx, b)
defer container.CleanUp(ctx)
- // Directory and filename inside container where fio will read/write.
- outdir := "/data"
- outfile := filepath.Join(outdir, "test.txt")
-
- // Make the required mount and grab a cleanup for bind mounts
- // as they are backed by a temp directory (mktemp).
- mnt, mountCleanup, err := makeMount(machine, fsType, outdir)
+ mnts, outdir, mountCleanup, err := harness.MakeMount(machine, fsType)
if err != nil {
b.Fatalf("failed to make mount: %v", err)
}
@@ -109,12 +102,9 @@ func BenchmarkFio(b *testing.B) {
// Start the container with the mount.
if err := container.Spawn(
- ctx,
- dockerutil.RunOpts{
- Image: "benchmarks/fio",
- Mounts: []mount.Mount{
- mnt,
- },
+ ctx, dockerutil.RunOpts{
+ Image: "benchmarks/fio",
+ Mounts: mnts,
},
// Sleep on the order of b.N.
"sleep", fmt.Sprintf("%d", 1000*b.N),
@@ -122,6 +112,9 @@ func BenchmarkFio(b *testing.B) {
b.Fatalf("failed to start fio container with: %v", err)
}
+ // Directory and filename inside container where fio will read/write.
+ outfile := filepath.Join(outdir, "test.txt")
+
// For reads, we need a file to read so make one inside the container.
if strings.Contains(tc.Test, "read") {
fallocateCmd := fmt.Sprintf("fallocate -l %dK %s", tc.Size, outfile)
@@ -135,6 +128,7 @@ func BenchmarkFio(b *testing.B) {
if err := harness.DropCaches(machine); err != nil {
b.Skipf("failed to drop caches with %v. You probably need root.", err)
}
+
cmd := tc.MakeCmd(outfile)
if err := harness.DropCaches(machine); err != nil {
@@ -154,39 +148,6 @@ func BenchmarkFio(b *testing.B) {
}
}
-// makeMount makes a mount and cleanup based on the requested type. Bind
-// and volume mounts are backed by a temp directory made with mktemp.
-// tmpfs mounts require no such backing and are just made.
-// It is up to the caller to call the returned cleanup.
-func makeMount(machine harness.Machine, mountType mount.Type, target string) (mount.Mount, func(), error) {
- switch mountType {
- case mount.TypeVolume, mount.TypeBind:
- dir, err := machine.RunCommand("mktemp", "-d")
- if err != nil {
- return mount.Mount{}, func() {}, fmt.Errorf("failed to create tempdir: %v", err)
- }
- dir = strings.TrimSuffix(dir, "\n")
-
- out, err := machine.RunCommand("chmod", "777", dir)
- if err != nil {
- machine.RunCommand("rm", "-rf", dir)
- return mount.Mount{}, func() {}, fmt.Errorf("failed modify directory: %v %s", err, out)
- }
- return mount.Mount{
- Target: target,
- Source: dir,
- Type: mount.TypeBind,
- }, func() { machine.RunCommand("rm", "-rf", dir) }, nil
- case mount.TypeTmpfs:
- return mount.Mount{
- Target: target,
- Type: mount.TypeTmpfs,
- }, func() {}, nil
- default:
- return mount.Mount{}, func() {}, fmt.Errorf("illegal mount time not supported: %v", mountType)
- }
-}
-
// TestMain is the main method for package fs.
func TestMain(m *testing.M) {
harness.Init()
diff --git a/test/benchmarks/harness/BUILD b/test/benchmarks/harness/BUILD
index c2e316709..116610938 100644
--- a/test/benchmarks/harness/BUILD
+++ b/test/benchmarks/harness/BUILD
@@ -14,5 +14,6 @@ go_library(
deps = [
"//pkg/test/dockerutil",
"//pkg/test/testutil",
+ "@com_github_docker_docker//api/types/mount:go_default_library",
],
)
diff --git a/test/benchmarks/harness/util.go b/test/benchmarks/harness/util.go
index aeac7ebff..36abe1069 100644
--- a/test/benchmarks/harness/util.go
+++ b/test/benchmarks/harness/util.go
@@ -18,8 +18,10 @@ import (
"context"
"fmt"
"net"
+ "strings"
"testing"
+ "github.com/docker/docker/api/types/mount"
"gvisor.dev/gvisor/pkg/test/dockerutil"
"gvisor.dev/gvisor/pkg/test/testutil"
)
@@ -55,3 +57,53 @@ func DebugLog(b *testing.B, msg string, args ...interface{}) {
b.Logf(msg, args...)
}
}
+
+const (
+ // BindFS indicates a bind mount should be created.
+ BindFS = "bindfs"
+ // TmpFS indicates a tmpfs mount should be created.
+ TmpFS = "tmpfs"
+ // RootFS indicates no mount should be created and the root mount should be used.
+ RootFS = "rootfs"
+)
+
+// MakeMount makes a mount and cleanup based on the requested type. Bind
+// and volume mounts are backed by a temp directory made with mktemp.
+// tmpfs mounts require no such backing and are just made.
+// rootfs mounts do not make a mount, but instead return a target direectory at root.
+// It is up to the caller to call the returned cleanup.
+func MakeMount(machine Machine, fsType string) ([]mount.Mount, string, func(), error) {
+ mounts := make([]mount.Mount, 0, 1)
+ switch fsType {
+ case BindFS:
+ dir, err := machine.RunCommand("mktemp", "-d")
+ if err != nil {
+ return mounts, "", func() {}, fmt.Errorf("failed to create tempdir: %v", err)
+ }
+ dir = strings.TrimSuffix(dir, "\n")
+
+ out, err := machine.RunCommand("chmod", "777", dir)
+ if err != nil {
+ machine.RunCommand("rm", "-rf", dir)
+ return mounts, "", func() {}, fmt.Errorf("failed modify directory: %v %s", err, out)
+ }
+ target := "/data"
+ mounts = append(mounts, mount.Mount{
+ Target: target,
+ Source: dir,
+ Type: mount.TypeBind,
+ })
+ return mounts, target, func() { machine.RunCommand("rm", "-rf", dir) }, nil
+ case RootFS:
+ return mounts, "/", func() {}, nil
+ case TmpFS:
+ target := "/data"
+ mounts = append(mounts, mount.Mount{
+ Target: target,
+ Type: mount.TypeTmpfs,
+ })
+ return mounts, target, func() {}, nil
+ default:
+ return mounts, "", func() {}, fmt.Errorf("illegal mount type not supported: %v", fsType)
+ }
+}
diff --git a/test/benchmarks/tcp/tcp_proxy.go b/test/benchmarks/tcp/tcp_proxy.go
index 780e4f7ae..be74e4d4a 100644
--- a/test/benchmarks/tcp/tcp_proxy.go
+++ b/test/benchmarks/tcp/tcp_proxy.go
@@ -29,7 +29,6 @@ import (
"runtime"
"runtime/pprof"
"strconv"
- "syscall"
"time"
"golang.org/x/sys/unix"
@@ -112,33 +111,33 @@ func setupNetwork(ifaceName string, numChannels int) (fds []int, err error) {
const protocol = 0x0300 // htons(ETH_P_ALL)
fds := make([]int, numChannels)
for i := range fds {
- fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, protocol)
+ fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, protocol)
if err != nil {
return nil, fmt.Errorf("unable to create raw socket: %v", err)
}
// Bind to the appropriate device.
- ll := syscall.SockaddrLinklayer{
+ ll := unix.SockaddrLinklayer{
Protocol: protocol,
Ifindex: iface.Index,
- Pkttype: syscall.PACKET_HOST,
+ Pkttype: unix.PACKET_HOST,
}
- if err := syscall.Bind(fd, &ll); err != nil {
+ if err := unix.Bind(fd, &ll); err != nil {
return nil, fmt.Errorf("unable to bind to %q: %v", iface.Name, err)
}
// RAW Sockets by default have a very small SO_RCVBUF of 256KB,
// up it to at least 4MB to reduce packet drops.
- if err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bufSize); err != nil {
+ if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_RCVBUF, bufSize); err != nil {
return nil, fmt.Errorf("setsockopt(..., SO_RCVBUF, %v,..) = %v", bufSize, err)
}
- if err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bufSize); err != nil {
+ if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_SNDBUF, bufSize); err != nil {
return nil, fmt.Errorf("setsockopt(..., SO_SNDBUF, %v,..) = %v", bufSize, err)
}
if !*swgso && *gso != 0 {
- if err := syscall.SetsockoptInt(fd, syscall.SOL_PACKET, unix.PACKET_VNET_HDR, 1); err != nil {
+ if err := unix.SetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_VNET_HDR, 1); err != nil {
return nil, fmt.Errorf("unable to enable the PACKET_VNET_HDR option: %v", err)
}
}
@@ -403,7 +402,7 @@ func main() {
log.Printf("client=%v, server=%v, ready.", *client, *server)
sigs := make(chan os.Signal, 1)
- signal.Notify(sigs, syscall.SIGTERM)
+ signal.Notify(sigs, unix.SIGTERM)
go func() {
<-sigs
if *cpuprofile != "" {
diff --git a/test/fuse/linux/mount_test.cc b/test/fuse/linux/mount_test.cc
index 8a5478116..276f842ea 100644
--- a/test/fuse/linux/mount_test.cc
+++ b/test/fuse/linux/mount_test.cc
@@ -15,6 +15,7 @@
#include <errno.h>
#include <fcntl.h>
#include <sys/mount.h>
+#include <unistd.h>
#include "gtest/gtest.h"
#include "test/util/mount_util.h"
@@ -29,7 +30,9 @@ namespace {
TEST(FuseMount, Success) {
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/fuse", O_WRONLY));
- std::string mopts = absl::StrCat("fd=", std::to_string(fd.get()));
+ std::string mopts =
+ absl::StrFormat("fd=%d,user_id=%d,group_id=%d,rootmode=0777", fd.get(),
+ getuid(), getgid());
const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
diff --git a/test/iptables/BUILD b/test/iptables/BUILD
index ae4bba847..94d4ca2d4 100644
--- a/test/iptables/BUILD
+++ b/test/iptables/BUILD
@@ -18,6 +18,7 @@ go_library(
"//pkg/binary",
"//pkg/test/testutil",
"//pkg/usermem",
+ "@org_golang_x_sys//unix:go_default_library",
],
)
diff --git a/test/iptables/iptables_unsafe.go b/test/iptables/iptables_unsafe.go
index bd85a8fea..dd1a1c082 100644
--- a/test/iptables/iptables_unsafe.go
+++ b/test/iptables/iptables_unsafe.go
@@ -16,12 +16,13 @@ package iptables
import (
"fmt"
- "syscall"
"unsafe"
+
+ "golang.org/x/sys/unix"
)
type originalDstError struct {
- errno syscall.Errno
+ errno unix.Errno
}
func (e originalDstError) Error() string {
@@ -32,27 +33,27 @@ func (e originalDstError) Error() string {
// getsockopt.
const SO_ORIGINAL_DST = 80
-func originalDestination4(connfd int) (syscall.RawSockaddrInet4, error) {
- var addr syscall.RawSockaddrInet4
- var addrLen uint32 = syscall.SizeofSockaddrInet4
- if errno := originalDestination(connfd, syscall.SOL_IP, unsafe.Pointer(&addr), &addrLen); errno != 0 {
- return syscall.RawSockaddrInet4{}, originalDstError{errno}
+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) (syscall.RawSockaddrInet6, error) {
- var addr syscall.RawSockaddrInet6
- var addrLen uint32 = syscall.SizeofSockaddrInet6
- if errno := originalDestination(connfd, syscall.SOL_IPV6, unsafe.Pointer(&addr), &addrLen); errno != 0 {
- return syscall.RawSockaddrInet6{}, originalDstError{errno}
+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) syscall.Errno {
- _, _, errno := syscall.Syscall6(
- syscall.SYS_GETSOCKOPT,
+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,
diff --git a/test/iptables/nat.go b/test/iptables/nat.go
index 7f1d6d7ad..70d8a1832 100644
--- a/test/iptables/nat.go
+++ b/test/iptables/nat.go
@@ -19,8 +19,8 @@ import (
"errors"
"fmt"
"net"
- "syscall"
+ "golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/binary"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -584,33 +584,33 @@ func listenForRedirectedConn(ctx context.Context, ipv6 bool, originalDsts []net.
// traditional syscalls.
// Create the listening socket, bind, listen, and accept.
- family := syscall.AF_INET
+ family := unix.AF_INET
if ipv6 {
- family = syscall.AF_INET6
+ family = unix.AF_INET6
}
- sockfd, err := syscall.Socket(family, syscall.SOCK_STREAM, 0)
+ sockfd, err := unix.Socket(family, unix.SOCK_STREAM, 0)
if err != nil {
return err
}
- defer syscall.Close(sockfd)
+ defer unix.Close(sockfd)
- var bindAddr syscall.Sockaddr
+ var bindAddr unix.Sockaddr
if ipv6 {
- bindAddr = &syscall.SockaddrInet6{
+ 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 = &syscall.SockaddrInet4{
+ bindAddr = &unix.SockaddrInet4{
Port: acceptPort,
Addr: [4]byte{0, 0, 0, 0}, // INADDR_ANY
}
}
- if err := syscall.Bind(sockfd, bindAddr); err != nil {
+ if err := unix.Bind(sockfd, bindAddr); err != nil {
return err
}
- if err := syscall.Listen(sockfd, 1); err != nil {
+ if err := unix.Listen(sockfd, 1); err != nil {
return err
}
@@ -619,8 +619,8 @@ func listenForRedirectedConn(ctx context.Context, ipv6 bool, originalDsts []net.
errCh := make(chan error)
go func() {
for {
- connFD, _, err := syscall.Accept(sockfd)
- if errors.Is(err, syscall.EINTR) {
+ connFD, _, err := unix.Accept(sockfd)
+ if errors.Is(err, unix.EINTR) {
continue
}
if err != nil {
@@ -641,7 +641,7 @@ func listenForRedirectedConn(ctx context.Context, ipv6 bool, originalDsts []net.
return err
case connFD = <-connCh:
}
- defer syscall.Close(connFD)
+ defer unix.Close(connFD)
// Verify that, despite listening on acceptPort, SO_ORIGINAL_DST
// indicates the packet was sent to originalDst:dropPort.
@@ -764,35 +764,35 @@ func recvWithRECVORIGDSTADDR(ctx context.Context, ipv6 bool, expectedDst *net.IP
// Create the listening socket.
var (
- family = syscall.AF_INET
- level = syscall.SOL_IP
- option = syscall.IP_RECVORIGDSTADDR
- bindAddr syscall.Sockaddr = &syscall.SockaddrInet4{
+ 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 = syscall.AF_INET6
- level = syscall.SOL_IPV6
+ family = unix.AF_INET6
+ level = unix.SOL_IPV6
option = 74 // IPV6_RECVORIGDSTADDR, which is missing from the syscall package.
- bindAddr = &syscall.SockaddrInet6{
+ 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 := syscall.Socket(family, syscall.SOCK_DGRAM, 0)
+ sockfd, err := unix.Socket(family, unix.SOCK_DGRAM, 0)
if err != nil {
- return fmt.Errorf("failed Socket(%d, %d, 0): %w", family, syscall.SOCK_DGRAM, err)
+ return fmt.Errorf("failed Socket(%d, %d, 0): %w", family, unix.SOCK_DGRAM, err)
}
- defer syscall.Close(sockfd)
+ defer unix.Close(sockfd)
- if err := syscall.Bind(sockfd, bindAddr); err != nil {
+ if err := unix.Bind(sockfd, bindAddr); err != nil {
return fmt.Errorf("failed Bind(%d, %+v): %v", sockfd, bindAddr, err)
}
// Enable IP_RECVORIGDSTADDR.
- if err := syscall.SetsockoptInt(sockfd, level, option, 1); err != nil {
+ if err := unix.SetsockoptInt(sockfd, level, option, 1); err != nil {
return fmt.Errorf("failed SetsockoptByte(%d, %d, %d, 1): %v", sockfd, level, option, err)
}
@@ -837,41 +837,41 @@ func recvWithRECVORIGDSTADDR(ctx context.Context, ipv6 bool, expectedDst *net.IP
// Verify that the address has the post-NAT port and address.
if ipv6 {
- return addrMatches6(addr.(syscall.RawSockaddrInet6), localAddrs, redirectPort)
+ return addrMatches6(addr.(unix.RawSockaddrInet6), localAddrs, redirectPort)
}
- return addrMatches4(addr.(syscall.RawSockaddrInet4), localAddrs, redirectPort)
+ return addrMatches4(addr.(unix.RawSockaddrInet4), localAddrs, redirectPort)
}
-func recvOrigDstAddr4(sockfd int) (syscall.RawSockaddrInet4, error) {
- buf, err := recvOrigDstAddr(sockfd, syscall.SOL_IP, syscall.SizeofSockaddrInet4)
+func recvOrigDstAddr4(sockfd int) (unix.RawSockaddrInet4, error) {
+ buf, err := recvOrigDstAddr(sockfd, unix.SOL_IP, unix.SizeofSockaddrInet4)
if err != nil {
- return syscall.RawSockaddrInet4{}, err
+ return unix.RawSockaddrInet4{}, err
}
- var addr syscall.RawSockaddrInet4
+ var addr unix.RawSockaddrInet4
binary.Unmarshal(buf, usermem.ByteOrder, &addr)
return addr, nil
}
-func recvOrigDstAddr6(sockfd int) (syscall.RawSockaddrInet6, error) {
- buf, err := recvOrigDstAddr(sockfd, syscall.SOL_IP, syscall.SizeofSockaddrInet6)
+func recvOrigDstAddr6(sockfd int) (unix.RawSockaddrInet6, error) {
+ buf, err := recvOrigDstAddr(sockfd, unix.SOL_IP, unix.SizeofSockaddrInet6)
if err != nil {
- return syscall.RawSockaddrInet6{}, err
+ return unix.RawSockaddrInet6{}, err
}
- var addr syscall.RawSockaddrInet6
+ 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, syscall.CmsgSpace(addrSize))
+ oob := make([]byte, unix.CmsgSpace(addrSize))
for {
- _, oobn, _, _, err := syscall.Recvmsg(
+ _, oobn, _, _, err := unix.Recvmsg(
sockfd,
buf, // Message buffer.
oob, // Out-of-band buffer.
0) // Flags.
- if errors.Is(err, syscall.EINTR) {
+ if errors.Is(err, unix.EINTR) {
continue
}
if err != nil {
@@ -880,7 +880,7 @@ func recvOrigDstAddr(sockfd int, level uintptr, addrSize int) ([]byte, error) {
oob = oob[:oobn]
// Parse out the control message.
- msgs, err := syscall.ParseSocketControlMessage(oob)
+ msgs, err := unix.ParseSocketControlMessage(oob)
if err != nil {
return nil, fmt.Errorf("failed to parse control message: %w", err)
}
@@ -888,10 +888,10 @@ func recvOrigDstAddr(sockfd int, level uintptr, addrSize int) ([]byte, error) {
}
}
-func addrMatches4(got syscall.RawSockaddrInet4, wantAddrs []net.IP, port uint16) error {
+func addrMatches4(got unix.RawSockaddrInet4, wantAddrs []net.IP, port uint16) error {
for _, wantAddr := range wantAddrs {
- want := syscall.RawSockaddrInet4{
- Family: syscall.AF_INET,
+ want := unix.RawSockaddrInet4{
+ Family: unix.AF_INET,
Port: htons(port),
}
copy(want.Addr[:], wantAddr.To4())
@@ -902,10 +902,10 @@ func addrMatches4(got syscall.RawSockaddrInet4, wantAddrs []net.IP, port uint16)
return fmt.Errorf("got %+v, but wanted one of %+v (note: port numbers are in network byte order)", got, wantAddrs)
}
-func addrMatches6(got syscall.RawSockaddrInet6, wantAddrs []net.IP, port uint16) error {
+func addrMatches6(got unix.RawSockaddrInet6, wantAddrs []net.IP, port uint16) error {
for _, wantAddr := range wantAddrs {
- want := syscall.RawSockaddrInet6{
- Family: syscall.AF_INET6,
+ want := unix.RawSockaddrInet6{
+ Family: unix.AF_INET6,
Port: htons(port),
}
copy(want.Addr[:], wantAddr.To16())
diff --git a/test/packetimpact/dut/posix_server.cc b/test/packetimpact/dut/posix_server.cc
index 0d93b806e..ea83bbe72 100644
--- a/test/packetimpact/dut/posix_server.cc
+++ b/test/packetimpact/dut/posix_server.cc
@@ -395,7 +395,8 @@ class PosixImpl final : public posix_server::Posix::Service {
::grpc::Status Shutdown(grpc::ServerContext *context,
const ::posix_server::ShutdownRequest *request,
::posix_server::ShutdownResponse *response) override {
- if (shutdown(request->fd(), request->how()) < 0) {
+ response->set_ret(shutdown(request->fd(), request->how()));
+ if (response->ret() < 0) {
response->set_errno_(errno);
}
return ::grpc::Status::OK;
diff --git a/test/packetimpact/proto/posix_server.proto b/test/packetimpact/proto/posix_server.proto
index 521f03465..175a65336 100644
--- a/test/packetimpact/proto/posix_server.proto
+++ b/test/packetimpact/proto/posix_server.proto
@@ -214,7 +214,8 @@ message ShutdownRequest {
}
message ShutdownResponse {
- int32 errno_ = 1; // "errno" may fail to compile in c++.
+ int32 ret = 1;
+ int32 errno_ = 2; // "errno" may fail to compile in c++.
}
message RecvRequest {
diff --git a/test/packetimpact/runner/defs.bzl b/test/packetimpact/runner/defs.bzl
index 8ce5edf2b..567f64c41 100644
--- a/test/packetimpact/runner/defs.bzl
+++ b/test/packetimpact/runner/defs.bzl
@@ -203,6 +203,11 @@ ALL_TESTS = [
name = "tcp_outside_the_window",
),
PacketimpactTestInfo(
+ name = "tcp_outside_the_window_closing",
+ # TODO(b/181625316): Fix netstack then merge into tcp_outside_the_window.
+ expect_netstack_failure = True,
+ ),
+ PacketimpactTestInfo(
name = "tcp_noaccept_close_rst",
),
PacketimpactTestInfo(
@@ -212,6 +217,11 @@ ALL_TESTS = [
name = "tcp_unacc_seq_ack",
),
PacketimpactTestInfo(
+ name = "tcp_unacc_seq_ack_closing",
+ # TODO(b/181625316): Fix netstack then merge into tcp_unacc_seq_ack.
+ expect_netstack_failure = True,
+ ),
+ PacketimpactTestInfo(
name = "tcp_paws_mechanism",
# TODO(b/156682000): Fix netstack then remove the line below.
expect_netstack_failure = True,
@@ -277,6 +287,11 @@ ALL_TESTS = [
PacketimpactTestInfo(
name = "tcp_info",
),
+ PacketimpactTestInfo(
+ name = "tcp_fin_retransmission",
+ # TODO(b/181625316): Fix netstack then remove the line below.
+ expect_netstack_failure = True,
+ ),
]
def validate_all_tests():
diff --git a/test/packetimpact/runner/dut.go b/test/packetimpact/runner/dut.go
index 3da265b78..1064ca976 100644
--- a/test/packetimpact/runner/dut.go
+++ b/test/packetimpact/runner/dut.go
@@ -109,6 +109,7 @@ type dutInfo struct {
dut DUT
ctrlNet, testNet *dockerutil.Network
netInfo *testbench.DUTTestNet
+ uname *testbench.DUTUname
}
// setUpDUT will set up one DUT and return information for setting up the
@@ -182,6 +183,10 @@ func setUpDUT(ctx context.Context, t *testing.T, id int, mkDevice func(*dockerut
POSIXServerIP: AddressInSubnet(DUTAddr, *ctrlNet.Subnet),
POSIXServerPort: CtrlPort,
}
+ info.uname, err = dut.Uname(ctx)
+ if err != nil {
+ return dutInfo{}, fmt.Errorf("failed to get uname information on DUT: %w", err)
+ }
return info, nil
}
@@ -195,7 +200,7 @@ func TestWithDUT(ctx context.Context, t *testing.T, mkDevice func(*dockerutil.Co
dutInfoChan := make(chan dutInfo, numDUTs)
errChan := make(chan error, numDUTs)
var dockerNetworks []*dockerutil.Network
- var dutTestNets []*testbench.DUTTestNet
+ var dutInfos []*testbench.DUTInfo
var duts []DUT
setUpCtx, cancelSetup := context.WithCancel(ctx)
@@ -214,7 +219,10 @@ func TestWithDUT(ctx context.Context, t *testing.T, mkDevice func(*dockerutil.Co
select {
case info := <-dutInfoChan:
dockerNetworks = append(dockerNetworks, info.ctrlNet, info.testNet)
- dutTestNets = append(dutTestNets, info.netInfo)
+ dutInfos = append(dutInfos, &testbench.DUTInfo{
+ Net: info.netInfo,
+ Uname: info.uname,
+ })
duts = append(duts, info.dut)
case err := <-errChan:
t.Fatal(err)
@@ -241,28 +249,29 @@ func TestWithDUT(ctx context.Context, t *testing.T, mkDevice func(*dockerutil.Co
testbenchContainer,
testbenchAddr,
dockerNetworks,
+ nil, /* sysctls */
"tail", "-f", "/dev/null",
); err != nil {
t.Fatalf("cannot start testbench container: %s", err)
}
- for i := range dutTestNets {
- name, info, err := deviceByIP(ctx, testbenchContainer, dutTestNets[i].LocalIPv4)
+ for i := range dutInfos {
+ name, info, err := deviceByIP(ctx, testbenchContainer, dutInfos[i].Net.LocalIPv4)
if err != nil {
- t.Fatalf("failed to get the device name associated with %s: %s", dutTestNets[i].LocalIPv4, err)
+ t.Fatalf("failed to get the device name associated with %s: %s", dutInfos[i].Net.LocalIPv4, err)
}
- dutTestNets[i].LocalDevName = name
- dutTestNets[i].LocalDevID = info.ID
- dutTestNets[i].LocalMAC = info.MAC
+ dutInfos[i].Net.LocalDevName = name
+ dutInfos[i].Net.LocalDevID = info.ID
+ dutInfos[i].Net.LocalMAC = info.MAC
localIPv6, err := getOrAssignIPv6Addr(ctx, testbenchContainer, name)
if err != nil {
t.Fatalf("failed to get IPV6 address on %s: %s", testbenchContainer.Name, err)
}
- dutTestNets[i].LocalIPv6 = localIPv6
+ dutInfos[i].Net.LocalIPv6 = localIPv6
}
- dutTestNetsBytes, err := json.Marshal(dutTestNets)
+ dutInfosBytes, err := json.Marshal(dutInfos)
if err != nil {
- t.Fatalf("failed to marshal %v into json: %s", dutTestNets, err)
+ t.Fatalf("failed to marshal %v into json: %s", dutInfos, err)
}
baseSnifferArgs := []string{
@@ -296,7 +305,8 @@ func TestWithDUT(ctx context.Context, t *testing.T, mkDevice func(*dockerutil.Co
"-n",
}
}
- for _, n := range dutTestNets {
+ for _, info := range dutInfos {
+ n := info.Net
snifferArgs := append(baseSnifferArgs, "-i", n.LocalDevName)
if !tshark {
snifferArgs = append(
@@ -351,7 +361,7 @@ func TestWithDUT(ctx context.Context, t *testing.T, mkDevice func(*dockerutil.Co
testArgs = append(testArgs, extraTestArgs...)
testArgs = append(testArgs,
fmt.Sprintf("--native=%t", native),
- "--dut_test_nets_json", string(dutTestNetsBytes),
+ "--dut_infos_json", string(dutInfosBytes),
)
testbenchLogs, err := testbenchContainer.Exec(ctx, dockerutil.ExecOpts{}, testArgs...)
if (err != nil) != expectFailure {
@@ -388,6 +398,10 @@ type DUT interface {
// The t parameter is supposed to be used for t.Cleanup. Don't use it for
// t.Fatal/FailNow functions.
Prepare(ctx context.Context, t *testing.T, runOpts dockerutil.RunOpts, ctrlNet, testNet *dockerutil.Network) (net.IP, net.HardwareAddr, uint32, string, error)
+
+ // Uname gathers information of DUT using command uname.
+ Uname(ctx context.Context) (*testbench.DUTUname, error)
+
// Logs retrieves the logs from the dut.
Logs(ctx context.Context) (string, error)
}
@@ -415,6 +429,10 @@ func (dut *DockerDUT) Prepare(ctx context.Context, _ *testing.T, runOpts dockeru
dut.c,
DUTAddr,
[]*dockerutil.Network{ctrlNet, testNet},
+ map[string]string{
+ // This enables creating ICMP sockets on Linux.
+ "net.ipv4.ping_group_range": "0 0",
+ },
containerPosixServerBinary,
"--ip=0.0.0.0",
fmt.Sprintf("--port=%d", CtrlPort),
@@ -440,6 +458,38 @@ func (dut *DockerDUT) Prepare(ctx context.Context, _ *testing.T, runOpts dockeru
return remoteIPv6, dutDeviceInfo.MAC, dutDeviceInfo.ID, testNetDev, nil
}
+// Uname implements DUT.Uname.
+func (dut *DockerDUT) Uname(ctx context.Context) (*testbench.DUTUname, error) {
+ machine, err := dut.c.Exec(ctx, dockerutil.ExecOpts{}, "uname", "-m")
+ if err != nil {
+ return nil, err
+ }
+ kernelRelease, err := dut.c.Exec(ctx, dockerutil.ExecOpts{}, "uname", "-r")
+ if err != nil {
+ return nil, err
+ }
+ kernelVersion, err := dut.c.Exec(ctx, dockerutil.ExecOpts{}, "uname", "-v")
+ if err != nil {
+ return nil, err
+ }
+ kernelName, err := dut.c.Exec(ctx, dockerutil.ExecOpts{}, "uname", "-s")
+ if err != nil {
+ return nil, err
+ }
+ // TODO(gvisor.dev/issues/5586): -o is not supported on macOS.
+ operatingSystem, err := dut.c.Exec(ctx, dockerutil.ExecOpts{}, "uname", "-o")
+ if err != nil {
+ return nil, err
+ }
+ return &testbench.DUTUname{
+ Machine: strings.TrimRight(machine, "\n"),
+ KernelName: strings.TrimRight(kernelName, "\n"),
+ KernelRelease: strings.TrimRight(kernelRelease, "\n"),
+ KernelVersion: strings.TrimRight(kernelVersion, "\n"),
+ OperatingSystem: strings.TrimRight(operatingSystem, "\n"),
+ }, nil
+}
+
// Logs implements DUT.Logs.
func (dut *DockerDUT) Logs(ctx context.Context) (string, error) {
logs, err := dut.c.Logs(ctx)
@@ -545,11 +595,14 @@ func createDockerNetwork(ctx context.Context, n *dockerutil.Network) error {
// StartContainer will create a container instance from runOpts, connect it
// with the specified docker networks and start executing the specified cmd.
-func StartContainer(ctx context.Context, runOpts dockerutil.RunOpts, c *dockerutil.Container, containerAddr net.IP, ns []*dockerutil.Network, cmd ...string) error {
+func StartContainer(ctx context.Context, runOpts dockerutil.RunOpts, c *dockerutil.Container, containerAddr net.IP, ns []*dockerutil.Network, sysctls map[string]string, cmd ...string) error {
conf, hostconf, netconf := c.ConfigsFrom(runOpts, cmd...)
_ = netconf
hostconf.AutoRemove = true
hostconf.Sysctls = map[string]string{"net.ipv6.conf.all.disable_ipv6": "0"}
+ for k, v := range sysctls {
+ hostconf.Sysctls[k] = v
+ }
if err := c.CreateFrom(ctx, runOpts.Image, conf, hostconf, nil); err != nil {
return fmt.Errorf("unable to create container %s: %w", c.Name, err)
diff --git a/test/packetimpact/testbench/BUILD b/test/packetimpact/testbench/BUILD
index 983c2c030..43b4c7ca1 100644
--- a/test/packetimpact/testbench/BUILD
+++ b/test/packetimpact/testbench/BUILD
@@ -1,7 +1,6 @@
load("//tools:defs.bzl", "go_library", "go_test")
package(
- default_visibility = ["//test/packetimpact:__subpackages__"],
licenses = ["notice"],
)
@@ -15,6 +14,7 @@ go_library(
"rawsockets.go",
"testbench.go",
],
+ visibility = ["//test/packetimpact:__subpackages__"],
deps = [
"//pkg/tcpip",
"//pkg/tcpip/buffer",
diff --git a/test/packetimpact/testbench/connections.go b/test/packetimpact/testbench/connections.go
index 15e1a51de..8ad9040ff 100644
--- a/test/packetimpact/testbench/connections.go
+++ b/test/packetimpact/testbench/connections.go
@@ -677,17 +677,17 @@ func (conn *TCPIPv4) Connect(t *testing.T) {
t.Helper()
// Send the SYN.
- conn.Send(t, TCP{Flags: Uint8(header.TCPFlagSyn)})
+ conn.Send(t, TCP{Flags: TCPFlags(header.TCPFlagSyn)})
// Wait for the SYN-ACK.
- synAck, err := conn.Expect(t, TCP{Flags: Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
+ synAck, err := conn.Expect(t, TCP{Flags: TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("didn't get synack during handshake: %s", err)
}
conn.layerStates[len(conn.layerStates)-1].(*tcpState).synAck = synAck
// Send an ACK.
- conn.Send(t, TCP{Flags: Uint8(header.TCPFlagAck)})
+ conn.Send(t, TCP{Flags: TCPFlags(header.TCPFlagAck)})
}
// ConnectWithOptions performs a TCP 3-way handshake with given TCP options.
@@ -696,17 +696,17 @@ func (conn *TCPIPv4) ConnectWithOptions(t *testing.T, options []byte) {
t.Helper()
// Send the SYN.
- conn.Send(t, TCP{Flags: Uint8(header.TCPFlagSyn), Options: options})
+ conn.Send(t, TCP{Flags: TCPFlags(header.TCPFlagSyn), Options: options})
// Wait for the SYN-ACK.
- synAck, err := conn.Expect(t, TCP{Flags: Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
+ synAck, err := conn.Expect(t, TCP{Flags: TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("didn't get synack during handshake: %s", err)
}
conn.layerStates[len(conn.layerStates)-1].(*tcpState).synAck = synAck
// Send an ACK.
- conn.Send(t, TCP{Flags: Uint8(header.TCPFlagAck)})
+ conn.Send(t, TCP{Flags: TCPFlags(header.TCPFlagAck)})
}
// ExpectData is a convenient method that expects a Layer and the Layer after
@@ -823,6 +823,27 @@ func (conn *TCPIPv4) LocalAddr(t *testing.T) *unix.SockaddrInet4 {
return sa
}
+// GenerateOTWSeqSegment generates a segment with
+// seqnum = RCV.NXT + RCV.WND + seqNumOffset, the generated segment is only
+// acceptable when seqNumOffset is 0, otherwise an ACK is expected from the
+// receiver.
+func GenerateOTWSeqSegment(t *testing.T, conn *TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) TCP {
+ t.Helper()
+ lastAcceptable := conn.LocalSeqNum(t).Add(windowSize)
+ otwSeq := uint32(lastAcceptable.Add(seqNumOffset))
+ return TCP{SeqNum: Uint32(otwSeq), Flags: TCPFlags(header.TCPFlagAck)}
+}
+
+// GenerateUnaccACKSegment generates a segment with
+// acknum = SND.NXT + seqNumOffset, the generated segment is only acceptable
+// when seqNumOffset is 0, otherwise an ACK is expected from the receiver.
+func GenerateUnaccACKSegment(t *testing.T, conn *TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) TCP {
+ t.Helper()
+ lastAcceptable := conn.RemoteSeqNum(t)
+ unaccAck := uint32(lastAcceptable.Add(seqNumOffset))
+ return TCP{AckNum: Uint32(unaccAck), Flags: TCPFlags(header.TCPFlagAck)}
+}
+
// IPv4Conn maintains the state for all the layers in a IPv4 connection.
type IPv4Conn struct {
Connection
diff --git a/test/packetimpact/testbench/dut.go b/test/packetimpact/testbench/dut.go
index be5121d98..eabdc8cb3 100644
--- a/test/packetimpact/testbench/dut.go
+++ b/test/packetimpact/testbench/dut.go
@@ -19,7 +19,6 @@ import (
"encoding/binary"
"fmt"
"net"
- "syscall"
"testing"
"time"
@@ -35,24 +34,26 @@ type DUT struct {
conn *grpc.ClientConn
posixServer POSIXClient
Net *DUTTestNet
+ Uname *DUTUname
}
// NewDUT creates a new connection with the DUT over gRPC.
func NewDUT(t *testing.T) DUT {
t.Helper()
- n := GetDUTTestNet()
- dut := n.ConnectToDUT(t)
+ info := getDUTInfo()
+ dut := info.ConnectToDUT(t)
t.Cleanup(func() {
dut.TearDownConnection()
- dut.Net.Release()
+ info.release()
})
return dut
}
// ConnectToDUT connects to DUT through gRPC.
-func (n *DUTTestNet) ConnectToDUT(t *testing.T) DUT {
+func (info *DUTInfo) ConnectToDUT(t *testing.T) DUT {
t.Helper()
+ n := info.Net
posixServerAddress := net.JoinHostPort(n.POSIXServerIP.String(), fmt.Sprintf("%d", n.POSIXServerPort))
conn, err := grpc.Dial(posixServerAddress, grpc.WithInsecure(), grpc.WithKeepaliveParams(keepalive.ClientParameters{Timeout: RPCKeepalive}))
if err != nil {
@@ -63,6 +64,7 @@ func (n *DUTTestNet) ConnectToDUT(t *testing.T) DUT {
conn: conn,
posixServer: posixServer,
Net: n,
+ Uname: info.Uname,
}
}
@@ -196,7 +198,7 @@ func (dut *DUT) AcceptWithErrno(ctx context.Context, t *testing.T, sockfd int32)
if err != nil {
t.Fatalf("failed to call Accept: %s", err)
}
- return resp.GetFd(), dut.protoToSockaddr(t, resp.GetAddr()), syscall.Errno(resp.GetErrno_())
+ return resp.GetFd(), dut.protoToSockaddr(t, resp.GetAddr()), unix.Errno(resp.GetErrno_())
}
// Bind calls bind on the DUT and causes a fatal test failure if it doesn't
@@ -225,7 +227,7 @@ func (dut *DUT) BindWithErrno(ctx context.Context, t *testing.T, fd int32, sa un
if err != nil {
t.Fatalf("failed to call Bind: %s", err)
}
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
+ return resp.GetRet(), unix.Errno(resp.GetErrno_())
}
// Close calls close on the DUT and causes a fatal test failure if it doesn't
@@ -253,7 +255,7 @@ func (dut *DUT) CloseWithErrno(ctx context.Context, t *testing.T, fd int32) (int
if err != nil {
t.Fatalf("failed to call Close: %s", err)
}
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
+ return resp.GetRet(), unix.Errno(resp.GetErrno_())
}
// Connect calls connect on the DUT and causes a fatal test failure if it
@@ -267,7 +269,7 @@ func (dut *DUT) Connect(t *testing.T, fd int32, sa unix.Sockaddr) {
ret, err := dut.ConnectWithErrno(ctx, t, fd, sa)
// Ignore 'operation in progress' error that can be returned when the socket
// is non-blocking.
- if err != syscall.Errno(unix.EINPROGRESS) && ret != 0 {
+ if err != unix.EINPROGRESS && ret != 0 {
t.Fatalf("failed to connect socket: %s", err)
}
}
@@ -284,7 +286,7 @@ func (dut *DUT) ConnectWithErrno(ctx context.Context, t *testing.T, fd int32, sa
if err != nil {
t.Fatalf("failed to call Connect: %s", err)
}
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
+ return resp.GetRet(), unix.Errno(resp.GetErrno_())
}
// GetSockName calls getsockname on the DUT and causes a fatal test failure if
@@ -313,7 +315,7 @@ func (dut *DUT) GetSockNameWithErrno(ctx context.Context, t *testing.T, sockfd i
if err != nil {
t.Fatalf("failed to call Bind: %s", err)
}
- return resp.GetRet(), dut.protoToSockaddr(t, resp.GetAddr()), syscall.Errno(resp.GetErrno_())
+ return resp.GetRet(), dut.protoToSockaddr(t, resp.GetAddr()), unix.Errno(resp.GetErrno_())
}
func (dut *DUT) getSockOpt(ctx context.Context, t *testing.T, sockfd, level, optname, optlen int32, typ pb.GetSockOptRequest_SockOptType) (int32, *pb.SockOptVal, error) {
@@ -334,7 +336,7 @@ func (dut *DUT) getSockOpt(ctx context.Context, t *testing.T, sockfd, level, opt
if optval == nil {
t.Fatalf("GetSockOpt response does not contain a value")
}
- return resp.GetRet(), optval, syscall.Errno(resp.GetErrno_())
+ return resp.GetRet(), optval, unix.Errno(resp.GetErrno_())
}
// GetSockOpt calls getsockopt on the DUT and causes a fatal test failure if it
@@ -452,7 +454,7 @@ func (dut *DUT) ListenWithErrno(ctx context.Context, t *testing.T, sockfd, backl
if err != nil {
t.Fatalf("failed to call Listen: %s", err)
}
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
+ return resp.GetRet(), unix.Errno(resp.GetErrno_())
}
// PollOne calls poll on the DUT and asserts that the expected event must be
@@ -519,7 +521,7 @@ func (dut *DUT) PollWithErrno(ctx context.Context, t *testing.T, pfds []unix.Pol
Revents: int16(protoPfd.GetEvents()),
})
}
- return resp.GetRet(), result, syscall.Errno(resp.GetErrno_())
+ return resp.GetRet(), result, unix.Errno(resp.GetErrno_())
}
// Send calls send on the DUT and causes a fatal test failure if it doesn't
@@ -550,7 +552,7 @@ func (dut *DUT) SendWithErrno(ctx context.Context, t *testing.T, sockfd int32, b
if err != nil {
t.Fatalf("failed to call Send: %s", err)
}
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
+ return resp.GetRet(), unix.Errno(resp.GetErrno_())
}
// SendTo calls sendto on the DUT and causes a fatal test failure if it doesn't
@@ -582,7 +584,7 @@ func (dut *DUT) SendToWithErrno(ctx context.Context, t *testing.T, sockfd int32,
if err != nil {
t.Fatalf("failed to call SendTo: %s", err)
}
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
+ return resp.GetRet(), unix.Errno(resp.GetErrno_())
}
// SetNonBlocking will set O_NONBLOCK flag for fd if nonblocking
@@ -602,7 +604,7 @@ func (dut *DUT) SetNonBlocking(t *testing.T, fd int32, nonblocking bool) {
t.Fatalf("failed to call SetNonblocking: %s", err)
}
if resp.GetRet() == -1 {
- t.Fatalf("fcntl(%d, %s) failed: %s", fd, resp.GetCmd(), syscall.Errno(resp.GetErrno_()))
+ t.Fatalf("fcntl(%d, %s) failed: %s", fd, resp.GetCmd(), unix.Errno(resp.GetErrno_()))
}
}
@@ -619,7 +621,7 @@ func (dut *DUT) setSockOpt(ctx context.Context, t *testing.T, sockfd, level, opt
if err != nil {
t.Fatalf("failed to call SetSockOpt: %s", err)
}
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
+ return resp.GetRet(), unix.Errno(resp.GetErrno_())
}
// SetSockOpt calls setsockopt on the DUT and causes a fatal test failure if it
@@ -720,7 +722,7 @@ func (dut *DUT) SocketWithErrno(t *testing.T, domain, typ, proto int32) (int32,
if err != nil {
t.Fatalf("failed to call Socket: %s", err)
}
- return resp.GetFd(), syscall.Errno(resp.GetErrno_())
+ return resp.GetFd(), unix.Errno(resp.GetErrno_())
}
// Recv calls recv on the DUT and causes a fatal test failure if it doesn't
@@ -751,7 +753,7 @@ func (dut *DUT) RecvWithErrno(ctx context.Context, t *testing.T, sockfd, len, fl
if err != nil {
t.Fatalf("failed to call Recv: %s", err)
}
- return resp.GetRet(), resp.GetBuf(), syscall.Errno(resp.GetErrno_())
+ return resp.GetRet(), resp.GetBuf(), unix.Errno(resp.GetErrno_())
}
// SetSockLingerOption sets SO_LINGER socket option on the DUT.
@@ -771,16 +773,19 @@ func (dut *DUT) SetSockLingerOption(t *testing.T, sockfd int32, timeout time.Dur
// Shutdown calls shutdown on the DUT and causes a fatal test failure if it
// doesn't succeed. If more control over the timeout or error handling is
// needed, use ShutdownWithErrno.
-func (dut *DUT) Shutdown(t *testing.T, fd, how int32) error {
+func (dut *DUT) Shutdown(t *testing.T, fd, how int32) {
t.Helper()
ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
- return dut.ShutdownWithErrno(ctx, t, fd, how)
+ ret, err := dut.ShutdownWithErrno(ctx, t, fd, how)
+ if ret != 0 {
+ t.Fatalf("failed to shutdown(%d, %d): %s", fd, how, err)
+ }
}
// ShutdownWithErrno calls shutdown on the DUT.
-func (dut *DUT) ShutdownWithErrno(ctx context.Context, t *testing.T, fd, how int32) error {
+func (dut *DUT) ShutdownWithErrno(ctx context.Context, t *testing.T, fd, how int32) (int32, error) {
t.Helper()
req := &pb.ShutdownRequest{
@@ -791,5 +796,8 @@ func (dut *DUT) ShutdownWithErrno(ctx context.Context, t *testing.T, fd, how int
if err != nil {
t.Fatalf("failed to call Shutdown: %s", err)
}
- return syscall.Errno(resp.GetErrno_())
+ if resp.GetErrno_() == 0 {
+ return resp.GetRet(), nil
+ }
+ return resp.GetRet(), unix.Errno(resp.GetErrno_())
}
diff --git a/test/packetimpact/testbench/layers.go b/test/packetimpact/testbench/layers.go
index 64a7a171a..2311f7686 100644
--- a/test/packetimpact/testbench/layers.go
+++ b/test/packetimpact/testbench/layers.go
@@ -407,6 +407,12 @@ func Uint8(v uint8) *uint8 {
return &v
}
+// TCPFlags is a helper routine that allocates a new
+// header.TCPFlags value to store v and returns a pointer to it.
+func TCPFlags(v header.TCPFlags) *header.TCPFlags {
+ return &v
+}
+
// Address is a helper routine that allocates a new tcpip.Address value to
// store v and returns a pointer to it.
func Address(v tcpip.Address) *tcpip.Address {
@@ -1030,7 +1036,7 @@ type TCP struct {
SeqNum *uint32
AckNum *uint32
DataOffset *uint8
- Flags *uint8
+ Flags *header.TCPFlags
WindowSize *uint16
Checksum *uint16
UrgentPointer *uint16
@@ -1063,7 +1069,7 @@ func (l *TCP) ToBytes() ([]byte, error) {
h.SetDataOffset(uint8(l.length()))
}
if l.Flags != nil {
- h.SetFlags(*l.Flags)
+ h.SetFlags(uint8(*l.Flags))
}
if l.WindowSize != nil {
h.SetWindowSize(*l.WindowSize)
@@ -1157,7 +1163,7 @@ func parseTCP(b []byte) (Layer, layerParser) {
SeqNum: Uint32(h.SequenceNumber()),
AckNum: Uint32(h.AckNumber()),
DataOffset: Uint8(h.DataOffset()),
- Flags: Uint8(h.Flags()),
+ Flags: TCPFlags(h.Flags()),
WindowSize: Uint16(h.WindowSize()),
Checksum: Uint16(h.Checksum()),
UrgentPointer: Uint16(h.UrgentPointer()),
diff --git a/test/packetimpact/testbench/layers_test.go b/test/packetimpact/testbench/layers_test.go
index eca0780b5..614a5de1e 100644
--- a/test/packetimpact/testbench/layers_test.go
+++ b/test/packetimpact/testbench/layers_test.go
@@ -178,7 +178,7 @@ func TestLayerStringFormat(t *testing.T) {
SeqNum: Uint32(3452155723),
AckNum: Uint32(2596996163),
DataOffset: Uint8(5),
- Flags: Uint8(20),
+ Flags: TCPFlags(header.TCPFlagRst | header.TCPFlagAck),
WindowSize: Uint16(64240),
Checksum: Uint16(0x2e2b),
},
@@ -188,7 +188,7 @@ func TestLayerStringFormat(t *testing.T) {
"SeqNum:3452155723 " +
"AckNum:2596996163 " +
"DataOffset:5 " +
- "Flags:20 " +
+ "Flags: R A " +
"WindowSize:64240 " +
"Checksum:11819" +
"}",
@@ -436,7 +436,7 @@ func TestTCPOptions(t *testing.T) {
DstPort: Uint16(54321),
SeqNum: Uint32(0),
AckNum: Uint32(0),
- Flags: Uint8(header.TCPFlagSyn),
+ Flags: TCPFlags(header.TCPFlagSyn),
WindowSize: Uint16(8192),
Checksum: Uint16(0xf51c),
UrgentPointer: Uint16(0),
@@ -480,7 +480,7 @@ func TestTCPOptions(t *testing.T) {
DstPort: Uint16(54321),
SeqNum: Uint32(0),
AckNum: Uint32(0),
- Flags: Uint8(header.TCPFlagSyn),
+ Flags: TCPFlags(header.TCPFlagSyn),
WindowSize: Uint16(8192),
Checksum: Uint16(0xe521),
UrgentPointer: Uint16(0),
diff --git a/test/packetimpact/testbench/testbench.go b/test/packetimpact/testbench/testbench.go
index 891897d55..a73c07e64 100644
--- a/test/packetimpact/testbench/testbench.go
+++ b/test/packetimpact/testbench/testbench.go
@@ -34,14 +34,29 @@ var (
// RPCTimeout is the gRPC timeout.
RPCTimeout = 100 * time.Millisecond
- // dutTestNetsJSON is the json string that describes all the test networks to
+ // dutInfosJSON is the json string that describes information about all the
// duts available to use.
- dutTestNetsJSON string
- // dutTestNets is the pool among which the testbench can choose a DUT to work
+ dutInfosJSON string
+ // dutInfo is the pool among which the testbench can choose a DUT to work
// with.
- dutTestNets chan *DUTTestNet
+ dutInfo chan *DUTInfo
)
+// DUTInfo has both network and uname information about the DUT.
+type DUTInfo struct {
+ Uname *DUTUname
+ Net *DUTTestNet
+}
+
+// DUTUname contains information about the DUT from uname.
+type DUTUname struct {
+ Machine string
+ KernelName string
+ KernelRelease string
+ KernelVersion string
+ OperatingSystem string
+}
+
// DUTTestNet describes the test network setup on dut and how the testbench
// should connect with an existing DUT.
type DUTTestNet struct {
@@ -86,7 +101,7 @@ func registerFlags(fs *flag.FlagSet) {
fs.BoolVar(&Native, "native", Native, "whether the test is running natively")
fs.DurationVar(&RPCTimeout, "rpc_timeout", RPCTimeout, "gRPC timeout")
fs.DurationVar(&RPCKeepalive, "rpc_keepalive", RPCKeepalive, "gRPC keepalive")
- fs.StringVar(&dutTestNetsJSON, "dut_test_nets_json", dutTestNetsJSON, "path to the dut test nets json file")
+ fs.StringVar(&dutInfosJSON, "dut_infos_json", dutInfosJSON, "json that describes the DUTs")
}
// Initialize initializes the testbench, it parse the flags and sets up the
@@ -94,27 +109,27 @@ func registerFlags(fs *flag.FlagSet) {
func Initialize(fs *flag.FlagSet) {
registerFlags(fs)
flag.Parse()
- if err := loadDUTTestNets(); err != nil {
+ if err := loadDUTInfos(); err != nil {
panic(err)
}
}
-// loadDUTTestNets loads available DUT test networks from the json file, it
+// loadDUTInfos loads available DUT test infos from the json file, it
// must be called after flag.Parse().
-func loadDUTTestNets() error {
- var parsedTestNets []DUTTestNet
- if err := json.Unmarshal([]byte(dutTestNetsJSON), &parsedTestNets); err != nil {
+func loadDUTInfos() error {
+ var dutInfos []DUTInfo
+ if err := json.Unmarshal([]byte(dutInfosJSON), &dutInfos); err != nil {
return fmt.Errorf("failed to unmarshal JSON: %w", err)
}
- if got, want := len(parsedTestNets), 1; got < want {
+ if got, want := len(dutInfos), 1; got < want {
return fmt.Errorf("got %d DUTs, the test requires at least %d DUTs", got, want)
}
// Using a buffered channel as semaphore
- dutTestNets = make(chan *DUTTestNet, len(parsedTestNets))
- for i := range parsedTestNets {
- parsedTestNets[i].LocalIPv4 = parsedTestNets[i].LocalIPv4.To4()
- parsedTestNets[i].RemoteIPv4 = parsedTestNets[i].RemoteIPv4.To4()
- dutTestNets <- &parsedTestNets[i]
+ dutInfo = make(chan *DUTInfo, len(dutInfos))
+ for i := range dutInfos {
+ dutInfos[i].Net.LocalIPv4 = dutInfos[i].Net.LocalIPv4.To4()
+ dutInfos[i].Net.RemoteIPv4 = dutInfos[i].Net.RemoteIPv4.To4()
+ dutInfo <- &dutInfos[i]
}
return nil
}
@@ -130,14 +145,13 @@ func GenerateRandomPayload(t *testing.T, n int) []byte {
return buf
}
-// GetDUTTestNet gets a usable DUTTestNet, the function will block until any
-// becomes available.
-func GetDUTTestNet() *DUTTestNet {
- return <-dutTestNets
+// getDUTInfo returns information about an available DUT from the pool. If no
+// DUT is readily available, getDUTInfo blocks until one becomes available.
+func getDUTInfo() *DUTInfo {
+ return <-dutInfo
}
-// Release releases the DUTTestNet back to the pool so that some other test
-// can use.
-func (n *DUTTestNet) Release() {
- dutTestNets <- n
+// release returns the DUTInfo back to the pool.
+func (info *DUTInfo) release() {
+ dutInfo <- info
}
diff --git a/test/packetimpact/tests/BUILD b/test/packetimpact/tests/BUILD
index 301cf4980..d5cb0ae06 100644
--- a/test/packetimpact/tests/BUILD
+++ b/test/packetimpact/tests/BUILD
@@ -124,6 +124,17 @@ packetimpact_testbench(
)
packetimpact_testbench(
+ name = "tcp_outside_the_window_closing",
+ srcs = ["tcp_outside_the_window_closing_test.go"],
+ deps = [
+ "//pkg/tcpip/header",
+ "//pkg/tcpip/seqnum",
+ "//test/packetimpact/testbench",
+ "@org_golang_x_sys//unix:go_default_library",
+ ],
+)
+
+packetimpact_testbench(
name = "tcp_noaccept_close_rst",
srcs = ["tcp_noaccept_close_rst_test.go"],
deps = [
@@ -155,6 +166,17 @@ packetimpact_testbench(
)
packetimpact_testbench(
+ name = "tcp_unacc_seq_ack_closing",
+ srcs = ["tcp_unacc_seq_ack_closing_test.go"],
+ deps = [
+ "//pkg/tcpip/header",
+ "//pkg/tcpip/seqnum",
+ "//test/packetimpact/testbench",
+ "@org_golang_x_sys//unix:go_default_library",
+ ],
+)
+
+packetimpact_testbench(
name = "tcp_paws_mechanism",
srcs = ["tcp_paws_mechanism_test.go"],
deps = [
@@ -375,6 +397,16 @@ packetimpact_testbench(
],
)
+packetimpact_testbench(
+ name = "tcp_fin_retransmission",
+ srcs = ["tcp_fin_retransmission_test.go"],
+ deps = [
+ "//pkg/tcpip/header",
+ "//test/packetimpact/testbench",
+ "@org_golang_x_sys//unix:go_default_library",
+ ],
+)
+
validate_all_tests()
[packetimpact_go_test(
diff --git a/test/packetimpact/tests/fin_wait2_timeout_test.go b/test/packetimpact/tests/fin_wait2_timeout_test.go
index 11f0fcd1e..cff8ca51d 100644
--- a/test/packetimpact/tests/fin_wait2_timeout_test.go
+++ b/test/packetimpact/tests/fin_wait2_timeout_test.go
@@ -51,21 +51,21 @@ func TestFinWait2Timeout(t *testing.T) {
}
dut.Close(t, acceptFd)
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
t.Fatalf("expected a FIN-ACK within 1 second but got none: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
time.Sleep(5 * time.Second)
conn.Drain(t)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
if tt.linger2 {
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, time.Second); err != nil {
t.Fatalf("expected a RST packet within a second but got none: %s", err)
}
} else {
- if got, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, 10*time.Second); got != nil || err == nil {
+ if got, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, 10*time.Second); got != nil || err == nil {
t.Fatalf("expected no RST packets within ten seconds but got one: %s", got)
}
}
diff --git a/test/packetimpact/tests/ipv4_id_uniqueness_test.go b/test/packetimpact/tests/ipv4_id_uniqueness_test.go
index a63b41366..2b69ceecb 100644
--- a/test/packetimpact/tests/ipv4_id_uniqueness_test.go
+++ b/test/packetimpact/tests/ipv4_id_uniqueness_test.go
@@ -100,7 +100,7 @@ func TestIPv4RetransmitIdentificationUniqueness(t *testing.T) {
// Let the DUT estimate RTO with RTT from the DATA-ACK.
// TODO(gvisor.dev/issue/2685) Estimate RTO during handshake, after which
// we can skip sending this ACK.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
dut.Send(t, remoteFD, tc.payload, 0)
expectTCP := &testbench.TCP{SeqNum: testbench.Uint32(uint32(*conn.RemoteSeqNum(t)))}
diff --git a/test/packetimpact/tests/tcp_cork_mss_test.go b/test/packetimpact/tests/tcp_cork_mss_test.go
index a7ba5035e..1db3c9883 100644
--- a/test/packetimpact/tests/tcp_cork_mss_test.go
+++ b/test/packetimpact/tests/tcp_cork_mss_test.go
@@ -60,24 +60,24 @@ func TestTCPCorkMSS(t *testing.T) {
// Expect the segments to be coalesced and sent and capped to MSS.
expectedPayload := testbench.Payload{Bytes: expectedData[:mss]}
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &expectedPayload, time.Second); err != nil {
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, &expectedPayload, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
// Expect the coalesced segment to be split and transmitted.
expectedPayload = testbench.Payload{Bytes: expectedData[mss:]}
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &expectedPayload, time.Second); err != nil {
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, &expectedPayload, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
// Check for segments to *not* be held up because of TCP_CORK when
// the current send window is less than MSS.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(uint16(2 * len(sampleData)))})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), WindowSize: testbench.Uint16(uint16(2 * len(sampleData)))})
dut.Send(t, acceptFD, sampleData, 0)
dut.Send(t, acceptFD, sampleData, 0)
expectedPayload = testbench.Payload{Bytes: append(sampleData, sampleData...)}
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &expectedPayload, time.Second); err != nil {
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, &expectedPayload, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
}
diff --git a/test/packetimpact/tests/tcp_fin_retransmission_test.go b/test/packetimpact/tests/tcp_fin_retransmission_test.go
new file mode 100644
index 000000000..500f7a783
--- /dev/null
+++ b/test/packetimpact/tests/tcp_fin_retransmission_test.go
@@ -0,0 +1,87 @@
+// 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 tcp_fin_retransmission_test
+
+import (
+ "flag"
+ "testing"
+ "time"
+
+ "golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/tcpip/header"
+ "gvisor.dev/gvisor/test/packetimpact/testbench"
+)
+
+func init() {
+ testbench.Initialize(flag.CommandLine)
+}
+
+// TestTCPClosingFinRetransmission tests that TCP implementation should retransmit
+// FIN segment in CLOSING state.
+func TestTCPClosingFinRetransmission(t *testing.T) {
+ for _, tt := range []struct {
+ description string
+ flags header.TCPFlags
+ }{
+ {"CLOSING", header.TCPFlagAck | header.TCPFlagFin},
+ {"FIN_WAIT_1", header.TCPFlagAck},
+ } {
+ t.Run(tt.description, func(t *testing.T) {
+ dut := testbench.NewDUT(t)
+ listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
+ defer dut.Close(t, listenFD)
+ conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ defer conn.Close(t)
+ conn.Connect(t)
+ acceptFD, _ := dut.Accept(t, listenFD)
+ defer dut.Close(t, acceptFD)
+
+ // Give a chance for the dut to estimate RTO with RTT from the DATA-ACK.
+ // TODO(gvisor.dev/issue/2685) Estimate RTO during handshake, after which
+ // we can skip the next block of code.
+ sampleData := []byte("Sample Data")
+ if got, want := dut.Send(t, acceptFD, sampleData, 0), len(sampleData); int(got) != want {
+ t.Fatalf("got dut.Send(t, %d, %s, 0) = %d, want %d", acceptFD, sampleData, got, want)
+ }
+ if _, err := conn.ExpectData(t, &testbench.TCP{}, &testbench.Payload{Bytes: sampleData}, time.Second); err != nil {
+ t.Fatalf("expected payload was not received: %s", err)
+ }
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
+
+ dut.Shutdown(t, acceptFD, unix.SHUT_WR)
+
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
+ t.Fatalf("expected FINACK from DUT, but got none: %s", err)
+ }
+
+ // Do not ack the FIN from DUT so that we can test for retransmission.
+ seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1)
+ conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(tt.flags)})
+
+ if tt.flags&header.TCPFlagFin != 0 {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
+ t.Errorf("expected an ACK to our FIN, but got none: %s", err)
+ }
+ }
+
+ if _, err := conn.Expect(t, testbench.TCP{
+ SeqNum: seqNumForTheirFIN,
+ Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck),
+ }, time.Second); err != nil {
+ t.Errorf("expected retransmission of FIN from the DUT: %s", err)
+ }
+ })
+ }
+}
diff --git a/test/packetimpact/tests/tcp_handshake_window_size_test.go b/test/packetimpact/tests/tcp_handshake_window_size_test.go
index 5d1266f3c..668e0275c 100644
--- a/test/packetimpact/tests/tcp_handshake_window_size_test.go
+++ b/test/packetimpact/tests/tcp_handshake_window_size_test.go
@@ -38,8 +38,8 @@ func TestTCPHandshakeWindowSize(t *testing.T) {
defer conn.Close(t)
// Start handshake with zero window size.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn), WindowSize: testbench.Uint16(uint16(0))})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn), WindowSize: testbench.Uint16(uint16(0))})
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
t.Fatalf("expected SYN-ACK: %s", err)
}
// Update the advertised window size to a non-zero value with the ACK that
@@ -47,7 +47,7 @@ func TestTCPHandshakeWindowSize(t *testing.T) {
//
// Set the window size with MSB set and expect the dut to treat it as
// an unsigned value.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(uint16(1 << 15))})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), WindowSize: testbench.Uint16(uint16(1 << 15))})
acceptFd, _ := dut.Accept(t, listenFD)
defer dut.Close(t, acceptFd)
@@ -59,7 +59,7 @@ func TestTCPHandshakeWindowSize(t *testing.T) {
// expect the dut to honor the recently advertised non-zero window
// and actually send out the data instead of probing for zero window.
dut.Send(t, acceptFd, sampleData, 0)
- if _, err := conn.ExpectNextData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload, time.Second); err != nil {
+ if _, err := conn.ExpectNextData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
}
diff --git a/test/packetimpact/tests/tcp_info_test.go b/test/packetimpact/tests/tcp_info_test.go
index 69275e54b..3fc2c7fe5 100644
--- a/test/packetimpact/tests/tcp_info_test.go
+++ b/test/packetimpact/tests/tcp_info_test.go
@@ -51,7 +51,7 @@ func TestTCPInfo(t *testing.T) {
if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
info := linux.TCPInfo{}
infoBytes := dut.GetSockOpt(t, acceptFD, unix.SOL_TCP, unix.TCP_INFO, int32(linux.SizeOfTCPInfo))
diff --git a/test/packetimpact/tests/tcp_linger_test.go b/test/packetimpact/tests/tcp_linger_test.go
index bc4b64388..88942904d 100644
--- a/test/packetimpact/tests/tcp_linger_test.go
+++ b/test/packetimpact/tests/tcp_linger_test.go
@@ -17,7 +17,6 @@ package tcp_linger_test
import (
"context"
"flag"
- "syscall"
"testing"
"time"
@@ -58,10 +57,10 @@ func TestTCPLingerZeroTimeout(t *testing.T) {
dut.Close(t, acceptFD)
// If the linger timeout is set to zero, the DUT should send a RST.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)}, time.Second); err != nil {
t.Errorf("expected RST-ACK packet within a second but got none: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
}
// TestTCPLingerOff tests when SO_LINGER is not set. DUT should send FIN-ACK
@@ -75,10 +74,10 @@ func TestTCPLingerOff(t *testing.T) {
dut.Close(t, acceptFD)
// If SO_LINGER is not set, DUT should send a FIN-ACK.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
}
// TestTCPLingerNonZeroTimeout tests when SO_LINGER is set with non-zero timeout.
@@ -115,10 +114,10 @@ func TestTCPLingerNonZeroTimeout(t *testing.T) {
t.Errorf("expected close to return within a second, but returned later")
}
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
})
}
}
@@ -166,10 +165,10 @@ func TestTCPLingerSendNonZeroTimeout(t *testing.T) {
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
}
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
})
}
}
@@ -183,19 +182,19 @@ func TestTCPLingerShutdownZeroTimeout(t *testing.T) {
defer closeAll(t, dut, listenFD, conn)
dut.SetSockLingerOption(t, acceptFD, 0, true)
- dut.Shutdown(t, acceptFD, syscall.SHUT_RDWR)
+ dut.Shutdown(t, acceptFD, unix.SHUT_RDWR)
dut.Close(t, acceptFD)
// Shutdown will send FIN-ACK with read/write option.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
}
// If the linger timeout is set to zero, the DUT should send a RST.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)}, time.Second); err != nil {
t.Errorf("expected RST-ACK packet within a second but got none: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
}
// TestTCPLingerShutdownSendNonZeroTimeout tests SO_LINGER with shutdown() and
@@ -220,7 +219,7 @@ func TestTCPLingerShutdownSendNonZeroTimeout(t *testing.T) {
sampleData := []byte("Sample Data")
dut.Send(t, acceptFD, sampleData, 0)
- dut.Shutdown(t, acceptFD, syscall.SHUT_RDWR)
+ dut.Shutdown(t, acceptFD, unix.SHUT_RDWR)
// Increase timeout as Close will take longer time to
// return when SO_LINGER is set with non-zero timeout.
@@ -243,10 +242,10 @@ func TestTCPLingerShutdownSendNonZeroTimeout(t *testing.T) {
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
}
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
})
}
}
diff --git a/test/packetimpact/tests/tcp_network_unreachable_test.go b/test/packetimpact/tests/tcp_network_unreachable_test.go
index 53dc903e4..5168450ad 100644
--- a/test/packetimpact/tests/tcp_network_unreachable_test.go
+++ b/test/packetimpact/tests/tcp_network_unreachable_test.go
@@ -17,7 +17,6 @@ package tcp_synsent_reset_test
import (
"context"
"flag"
- "syscall"
"testing"
"time"
@@ -46,12 +45,12 @@ func TestTCPSynSentUnreachable(t *testing.T) {
defer cancel()
sa := unix.SockaddrInet4{Port: int(port)}
copy(sa.Addr[:], dut.Net.LocalIPv4)
- if _, err := dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != syscall.Errno(unix.EINPROGRESS) {
+ if _, err := dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != unix.EINPROGRESS {
t.Errorf("got connect() = %v, want EINPROGRESS", err)
}
// Get the SYN.
- tcpLayers, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)}, nil, time.Second)
+ tcpLayers, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, nil, time.Second)
if err != nil {
t.Fatalf("expected SYN: %s", err)
}
@@ -100,12 +99,12 @@ func TestTCPSynSentUnreachable6(t *testing.T) {
ZoneId: dut.Net.RemoteDevID,
}
copy(sa.Addr[:], dut.Net.LocalIPv6)
- if _, err := dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != syscall.Errno(unix.EINPROGRESS) {
+ if _, err := dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != unix.EINPROGRESS {
t.Errorf("got connect() = %v, want EINPROGRESS", err)
}
// Get the SYN.
- tcpLayers, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)}, nil, time.Second)
+ tcpLayers, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, nil, time.Second)
if err != nil {
t.Fatalf("expected SYN: %s", err)
}
@@ -156,7 +155,7 @@ func getConnectError(t *testing.T, dut *testbench.DUT, fd int32) error {
// failure).
dut.PollOne(t, fd, unix.POLLOUT, 10*time.Second)
if errno := dut.GetSockOptInt(t, fd, unix.SOL_SOCKET, unix.SO_ERROR); errno != 0 {
- return syscall.Errno(errno)
+ return unix.Errno(errno)
}
return nil
}
diff --git a/test/packetimpact/tests/tcp_noaccept_close_rst_test.go b/test/packetimpact/tests/tcp_noaccept_close_rst_test.go
index d2871df08..14eb7d93b 100644
--- a/test/packetimpact/tests/tcp_noaccept_close_rst_test.go
+++ b/test/packetimpact/tests/tcp_noaccept_close_rst_test.go
@@ -40,7 +40,7 @@ func TestTcpNoAcceptCloseReset(t *testing.T) {
// it will only respond RST instead of RST+ACK.
dut.PollOne(t, listenFd, unix.POLLIN, time.Second)
dut.Close(t, listenFd)
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)}, 1*time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)}, 1*time.Second); err != nil {
t.Fatalf("expected a RST-ACK packet but got none: %s", err)
}
}
diff --git a/test/packetimpact/tests/tcp_outside_the_window_closing_test.go b/test/packetimpact/tests/tcp_outside_the_window_closing_test.go
new file mode 100644
index 000000000..1097746c7
--- /dev/null
+++ b/test/packetimpact/tests/tcp_outside_the_window_closing_test.go
@@ -0,0 +1,86 @@
+// Copyright 2021 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 tcp_outside_the_window_closing_test
+
+import (
+ "flag"
+ "fmt"
+ "testing"
+ "time"
+
+ "golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/tcpip/header"
+ "gvisor.dev/gvisor/pkg/tcpip/seqnum"
+ "gvisor.dev/gvisor/test/packetimpact/testbench"
+)
+
+func init() {
+ testbench.Initialize(flag.CommandLine)
+}
+
+// TestAckOTWSeqInClosing tests that the DUT should send an ACK with
+// the right ACK number when receiving a packet with OTW Seq number
+// in CLOSING state. https://tools.ietf.org/html/rfc793#page-69
+func TestAckOTWSeqInClosing(t *testing.T) {
+ for seqNumOffset := seqnum.Size(0); seqNumOffset < 3; seqNumOffset++ {
+ for _, tt := range []struct {
+ description string
+ flags header.TCPFlags
+ payloads testbench.Layers
+ }{
+ {"SYN", header.TCPFlagSyn, nil},
+ {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil},
+ {"ACK", header.TCPFlagAck, nil},
+ {"FINACK", header.TCPFlagFin | header.TCPFlagAck, nil},
+ {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("abc123")}}},
+ } {
+ t.Run(fmt.Sprintf("%s%d", tt.description, seqNumOffset), func(t *testing.T) {
+ dut := testbench.NewDUT(t)
+ listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
+ defer dut.Close(t, listenFD)
+ conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ defer conn.Close(t)
+ conn.Connect(t)
+ acceptFD, _ := dut.Accept(t, listenFD)
+ defer dut.Close(t, acceptFD)
+
+ dut.Shutdown(t, acceptFD, unix.SHUT_WR)
+
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
+ t.Fatalf("expected FINACK from DUT, but got none: %s", err)
+ }
+
+ // Do not ack the FIN from DUT so that the TCP state on DUT is CLOSING instead of CLOSED.
+ seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1)
+ conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
+
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
+ t.Errorf("expected an ACK to our FIN, but got none: %s", err)
+ }
+
+ windowSize := seqnum.Size(*conn.SynAck(t).WindowSize) + seqNumOffset
+ conn.SendFrameStateless(t, conn.CreateFrame(t, testbench.Layers{&testbench.TCP{
+ SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))),
+ AckNum: seqNumForTheirFIN,
+ Flags: testbench.TCPFlags(tt.flags),
+ }}, tt.payloads...))
+
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
+ t.Errorf("expected an ACK but got none: %s", err)
+ }
+ })
+ }
+ }
+}
diff --git a/test/packetimpact/tests/tcp_outside_the_window_test.go b/test/packetimpact/tests/tcp_outside_the_window_test.go
index 8909a348e..7cd7ff703 100644
--- a/test/packetimpact/tests/tcp_outside_the_window_test.go
+++ b/test/packetimpact/tests/tcp_outside_the_window_test.go
@@ -37,7 +37,7 @@ func init() {
func TestTCPOutsideTheWindow(t *testing.T) {
for _, tt := range []struct {
description string
- tcpFlags uint8
+ tcpFlags header.TCPFlags
payload []testbench.Layer
seqNumOffset seqnum.Size
expectACK bool
@@ -76,11 +76,11 @@ func TestTCPOutsideTheWindow(t *testing.T) {
// to the AckNum.
localSeqNum := testbench.Uint32(uint32(*conn.LocalSeqNum(t)))
conn.Send(t, testbench.TCP{
- Flags: testbench.Uint8(tt.tcpFlags),
+ Flags: testbench.TCPFlags(tt.tcpFlags),
SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))),
}, tt.payload...)
- timeout := 3 * time.Second
- gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: localSeqNum}, timeout)
+ timeout := time.Second
+ gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: localSeqNum}, timeout)
if tt.expectACK && err != nil {
t.Fatalf("expected an ACK packet within %s but got none: %s", timeout, err)
}
@@ -93,11 +93,11 @@ func TestTCPOutsideTheWindow(t *testing.T) {
// has passed since the last ACK was sent.
t.Logf("sending another segment")
conn.Send(t, testbench.TCP{
- Flags: testbench.Uint8(tt.tcpFlags),
+ Flags: testbench.TCPFlags(tt.tcpFlags),
SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))),
}, tt.payload...)
timeout := 3 * time.Second
- gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: localSeqNum}, timeout)
+ gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: localSeqNum}, timeout)
if err == nil {
t.Fatalf("expected no ACK packet but got one: %s", gotACK)
}
diff --git a/test/packetimpact/tests/tcp_paws_mechanism_test.go b/test/packetimpact/tests/tcp_paws_mechanism_test.go
index 24d9ef4ec..9054955ea 100644
--- a/test/packetimpact/tests/tcp_paws_mechanism_test.go
+++ b/test/packetimpact/tests/tcp_paws_mechanism_test.go
@@ -38,8 +38,8 @@ func TestPAWSMechanism(t *testing.T) {
options := make([]byte, header.TCPOptionTSLength)
header.EncodeTSOption(currentTS(), 0, options)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn), Options: options})
- synAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn), Options: options})
+ synAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("didn't get synack during handshake: %s", err)
}
@@ -49,7 +49,7 @@ func TestPAWSMechanism(t *testing.T) {
}
tsecr := parsedSynOpts.TSVal
header.EncodeTSOption(currentTS(), tsecr, options)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), Options: options})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), Options: options})
acceptFD, _ := dut.Accept(t, listenFD)
defer dut.Close(t, acceptFD)
@@ -60,9 +60,9 @@ func TestPAWSMechanism(t *testing.T) {
// every time we send one, it should not cause any flakiness because timestamps
// only need to be non-decreasing.
time.Sleep(3 * time.Millisecond)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("expected an ACK but got none: %s", err)
}
@@ -85,9 +85,9 @@ func TestPAWSMechanism(t *testing.T) {
// 3ms here is chosen arbitrarily and this time.Sleep() should not cause flakiness
// due to the exact same reasoning discussed above.
time.Sleep(3 * time.Millisecond)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
- gotTCP, err = conn.Expect(t, testbench.TCP{AckNum: lastAckNum, Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ gotTCP, err = conn.Expect(t, testbench.TCP{AckNum: lastAckNum, Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("expected segment with AckNum %d but got none: %s", lastAckNum, err)
}
diff --git a/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go b/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
index 7dd1c326a..1c8b72ebe 100644
--- a/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
+++ b/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
@@ -21,7 +21,6 @@ import (
"errors"
"flag"
"sync"
- "syscall"
"testing"
"time"
@@ -45,10 +44,10 @@ func TestQueueSendInSynSentHandshake(t *testing.T) {
sampleData := []byte("Sample Data")
dut.SetNonBlocking(t, socket, true)
- if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, syscall.EINPROGRESS) {
+ if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, unix.EINPROGRESS) {
t.Fatalf("failed to bring DUT to SYN-SENT, got: %s, want EINPROGRESS", err)
}
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second); err != nil {
t.Fatalf("expected a SYN from DUT, but got none: %s", err)
}
@@ -85,19 +84,19 @@ func TestQueueSendInSynSentHandshake(t *testing.T) {
time.Sleep(100 * time.Millisecond)
// Bring the connection to Established.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)})
// Expect the data from the DUT's enqueued send request.
//
// On Linux, this can be piggybacked with the ACK completing the
// handshake. On gVisor, getting such a piggyback is a bit more
// complicated because the actual data enqueuing occurs in the
// callers of endpoint Write.
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagPsh | header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData}, time.Second); err != nil {
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagPsh | header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData}, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagPsh | header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagPsh | header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData})
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
t.Fatalf("expected an ACK from DUT, but got none: %s", err)
}
}
@@ -113,15 +112,15 @@ func TestQueueRecvInSynSentHandshake(t *testing.T) {
sampleData := []byte("Sample Data")
dut.SetNonBlocking(t, socket, true)
- if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, syscall.EINPROGRESS) {
+ if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, unix.EINPROGRESS) {
t.Fatalf("failed to bring DUT to SYN-SENT, got: %s, want EINPROGRESS", err)
}
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second); err != nil {
t.Fatalf("expected a SYN from DUT, but got none: %s", err)
}
- if _, _, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0); err != syscall.Errno(unix.EWOULDBLOCK) {
- t.Fatalf("expected error %s, got %s", syscall.Errno(unix.EWOULDBLOCK), err)
+ if _, _, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0); err != unix.EWOULDBLOCK {
+ t.Fatalf("expected error %s, got %s", unix.EWOULDBLOCK, err)
}
// Test blocking read.
@@ -160,14 +159,14 @@ func TestQueueRecvInSynSentHandshake(t *testing.T) {
time.Sleep(100 * time.Millisecond)
// Bring the connection to Established.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)})
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
t.Fatalf("expected an ACK from DUT, but got none: %s", err)
}
// Send sample payload so that DUT can recv.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagPsh | header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagPsh | header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData})
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
t.Fatalf("expected an ACK from DUT, but got none: %s", err)
}
}
@@ -183,10 +182,10 @@ func TestQueueSendInSynSentRST(t *testing.T) {
sampleData := []byte("Sample Data")
dut.SetNonBlocking(t, socket, true)
- if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, syscall.EINPROGRESS) {
+ if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, unix.EINPROGRESS) {
t.Fatalf("failed to bring DUT to SYN-SENT, got: %s, want EINPROGRESS", err)
}
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second); err != nil {
t.Fatalf("expected a SYN from DUT, but got none: %s", err)
}
@@ -207,8 +206,8 @@ func TestQueueSendInSynSentRST(t *testing.T) {
// Issue SEND call in SYN-SENT, this should be queued for
// process until the connection is established.
n, err := dut.SendWithErrno(ctx, t, socket, sampleData, 0)
- if err != syscall.Errno(unix.ECONNREFUSED) {
- t.Errorf("expected error %s, got %s", syscall.Errno(unix.ECONNREFUSED), err)
+ if err != unix.ECONNREFUSED {
+ t.Errorf("expected error %s, got %s", unix.ECONNREFUSED, err)
}
if n != -1 {
t.Errorf("expected return value %d, got %d", -1, n)
@@ -224,7 +223,7 @@ func TestQueueSendInSynSentRST(t *testing.T) {
// request and the system actually being blocked.
time.Sleep(100 * time.Millisecond)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)})
}
// TestQueueRecvInSynSentRST tests recv behavior when the TCP state
@@ -238,15 +237,15 @@ func TestQueueRecvInSynSentRST(t *testing.T) {
sampleData := []byte("Sample Data")
dut.SetNonBlocking(t, socket, true)
- if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, syscall.EINPROGRESS) {
+ if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, unix.EINPROGRESS) {
t.Fatalf("failed to bring DUT to SYN-SENT, got: %s, want EINPROGRESS", err)
}
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second); err != nil {
t.Fatalf("expected a SYN from DUT, but got none: %s", err)
}
- if _, _, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0); err != syscall.Errno(unix.EWOULDBLOCK) {
- t.Fatalf("expected error %s, got %s", syscall.Errno(unix.EWOULDBLOCK), err)
+ if _, _, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0); err != unix.EWOULDBLOCK {
+ t.Fatalf("expected error %s, got %s", unix.EWOULDBLOCK, err)
}
// Test blocking read.
@@ -266,8 +265,8 @@ func TestQueueRecvInSynSentRST(t *testing.T) {
// Issue RECEIVE call in SYN-SENT, this should be queued for
// process until the connection is established.
n, _, err := dut.RecvWithErrno(ctx, t, socket, int32(len(sampleData)), 0)
- if err != syscall.Errno(unix.ECONNREFUSED) {
- t.Errorf("expected error %s, got %s", syscall.Errno(unix.ECONNREFUSED), err)
+ if err != unix.ECONNREFUSED {
+ t.Errorf("expected error %s, got %s", unix.ECONNREFUSED, err)
}
if n != -1 {
t.Errorf("expected return value %d, got %d", -1, n)
@@ -283,5 +282,5 @@ func TestQueueRecvInSynSentRST(t *testing.T) {
// request and the system actually being blocked.
time.Sleep(100 * time.Millisecond)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)})
}
diff --git a/test/packetimpact/tests/tcp_rack_test.go b/test/packetimpact/tests/tcp_rack_test.go
index ef902c54d..0a5b0f12b 100644
--- a/test/packetimpact/tests/tcp_rack_test.go
+++ b/test/packetimpact/tests/tcp_rack_test.go
@@ -97,7 +97,7 @@ func sendAndReceive(t *testing.T, dut testbench.DUT, conn testbench.TCPIPv4, num
if sendACK {
time.Sleep(simulatedRTT)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(sn))})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(sn))})
}
}
return lastSent
@@ -149,7 +149,7 @@ func TestRACKTLPLost(t *testing.T) {
// Cumulative ACK for #[1-5] packets.
ackNum := seqNum1.Add(seqnum.Size(6 * payloadSize))
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(ackNum))})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(ackNum))})
// Probe Timeout (PTO) should be two times RTT. Check that the last
// packet is retransmitted after probe timeout.
@@ -194,7 +194,7 @@ func TestRACKWithSACK(t *testing.T) {
sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
start, end,
}}, sackBlock[sbOff:])
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
rtt, _ := getRTTAndRTO(t, dut, acceptFd)
timeout := 2 * rtt
@@ -206,7 +206,7 @@ func TestRACKWithSACK(t *testing.T) {
time.Sleep(simulatedRTT)
// ACK for #1 packet.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(end))})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(end))})
// RACK considers transmission times of the packets to mark them lost.
// As the 3rd packet was sent before the retransmitted 1st packet, RACK
@@ -243,7 +243,7 @@ func TestRACKWithoutReorder(t *testing.T) {
start, end,
}}, sackBlock[sbOff:])
time.Sleep(simulatedRTT)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
// RACK marks #1 and #2 packets as lost and retransmits both after
// RTT + reorderWindow. The reorderWindow initially will be a small
@@ -289,7 +289,7 @@ func TestRACKWithReorder(t *testing.T) {
sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
start, end,
}}, sackBlock[sbOff:])
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
}
// Send a DSACK block indicating both original and retransmitted
@@ -304,7 +304,7 @@ func TestRACKWithReorder(t *testing.T) {
dbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
start, end,
}}, dsackBlock[dbOff:])
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1 + numPkts*payloadSize)), Options: dsackBlock[:dbOff]})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1 + numPkts*payloadSize)), Options: dsackBlock[:dbOff]})
seqNum1.UpdateForward(seqnum.Size(numPkts * payloadSize))
sendTime := time.Now()
@@ -321,7 +321,7 @@ func TestRACKWithReorder(t *testing.T) {
sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
start, end,
}}, sackBlock[sbOff:])
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
// Expect the retransmission of #1 packet after RTT+ReorderWindow.
if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, time.Second); err != nil {
@@ -361,7 +361,7 @@ func TestRACKWithLostRetransmission(t *testing.T) {
start, end,
}}, sackBlock[sbOff:])
time.Sleep(simulatedRTT)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
// RACK marks #1 packet as lost and retransmits it after
// RTT + reorderWindow. The reorderWindow is bounded between a small
@@ -394,7 +394,7 @@ func TestRACKWithLostRetransmission(t *testing.T) {
start, end,
}}, sackBlock1[sbOff1:])
time.Sleep(simulatedRTT)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock1[:sbOff1]})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock1[:sbOff1]})
// Expect re-retransmission of #1 packet without entering an RTO.
if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, timeout); err != nil {
diff --git a/test/packetimpact/tests/tcp_rcv_buf_space_test.go b/test/packetimpact/tests/tcp_rcv_buf_space_test.go
index d6ad5cda6..f121d44eb 100644
--- a/test/packetimpact/tests/tcp_rcv_buf_space_test.go
+++ b/test/packetimpact/tests/tcp_rcv_buf_space_test.go
@@ -17,7 +17,6 @@ package tcp_rcv_buf_space_test
import (
"context"
"flag"
- "syscall"
"testing"
"golang.org/x/sys/unix"
@@ -61,7 +60,7 @@ func TestReduceRecvBuf(t *testing.T) {
payloadBytes = l
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, []testbench.Layer{&testbench.Payload{Bytes: payload[:payloadBytes]}}...)
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, []testbench.Layer{&testbench.Payload{Bytes: payload[:payloadBytes]}}...)
payload = payload[payloadBytes:]
}
@@ -73,7 +72,7 @@ func TestReduceRecvBuf(t *testing.T) {
// Second read should return EAGAIN as the last segment should have been
// dropped due to it exceeding the receive buffer space available in the
// socket.
- if ret, got, err := dut.RecvWithErrno(context.Background(), t, acceptFd, int32(len(sampleData)), syscall.MSG_DONTWAIT); got != nil || ret != -1 || err != syscall.EAGAIN {
+ if ret, got, err := dut.RecvWithErrno(context.Background(), t, acceptFd, int32(len(sampleData)), unix.MSG_DONTWAIT); got != nil || ret != -1 || err != unix.EAGAIN {
t.Fatalf("expected no packets but got: %s", got)
}
}
diff --git a/test/packetimpact/tests/tcp_retransmits_test.go b/test/packetimpact/tests/tcp_retransmits_test.go
index ba79fbf55..3dc8f63ab 100644
--- a/test/packetimpact/tests/tcp_retransmits_test.go
+++ b/test/packetimpact/tests/tcp_retransmits_test.go
@@ -15,6 +15,7 @@
package tcp_retransmits_test
import (
+ "bytes"
"flag"
"testing"
"time"
@@ -59,38 +60,43 @@ func TestRetransmits(t *testing.T) {
sampleData := []byte("Sample Data")
samplePayload := &testbench.Payload{Bytes: sampleData}
+ // Give a chance for the dut to estimate RTO with RTT from the DATA-ACK.
+ // This is to reduce the test run-time from the default initial RTO of 1s.
+ // TODO(gvisor.dev/issue/2685) Estimate RTO during handshake, after which
+ // we can skip this data send/recv which is solely to estimate RTO.
dut.Send(t, acceptFd, sampleData, 0)
if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
- // Give a chance for the dut to estimate RTO with RTT from the DATA-ACK.
- // TODO(gvisor.dev/issue/2685) Estimate RTO during handshake, after which
- // we can skip sending this ACK.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, nil, time.Second); err != nil {
+ t.Fatalf("expected packet was not received: %s", err)
+ }
+ // Wait for the DUT to receive the data, thus ensuring that the stack has
+ // estimated RTO before we query RTO via TCP_INFO.
+ if got := dut.Recv(t, acceptFd, int32(len(sampleData)), 0); !bytes.Equal(got, sampleData) {
+ t.Fatalf("got dut.Recv(t, %d, %d, 0) = %s, want %s", acceptFd, len(sampleData), got, sampleData)
+ }
const timeoutCorrection = time.Second
- const diffCorrection = time.Millisecond
+ const diffCorrection = 200 * time.Millisecond
rto := getRTO(t, dut, acceptFd)
- timeout := rto + timeoutCorrection
- startTime := time.Now()
dut.Send(t, acceptFd, sampleData, 0)
seq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)))
- if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: seq}, samplePayload, timeout); err != nil {
+ if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: seq}, samplePayload, rto+timeoutCorrection); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
// Expect retransmits of the same segment.
for i := 0; i < 5; i++ {
- if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: seq}, samplePayload, timeout); err != nil {
- t.Fatalf("expected payload was not received within %d loop %d err %s", timeout, i, err)
+ startTime := time.Now()
+ rto = getRTO(t, dut, acceptFd)
+ if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: seq}, samplePayload, rto+timeoutCorrection); err != nil {
+ t.Fatalf("expected payload was not received within %s loop %d err %s", rto+timeoutCorrection, i, err)
}
-
if diff := time.Since(startTime); diff+diffCorrection < rto {
- t.Fatalf("retransmit came sooner got: %d want: >= %d probe %d", diff, rto, i)
+ t.Fatalf("retransmit came sooner got: %s want: >= %s probe %d", diff+diffCorrection, rto, i)
}
- startTime = time.Now()
- rto = getRTO(t, dut, acceptFd)
- timeout = rto + timeoutCorrection
}
}
diff --git a/test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go b/test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go
index 418393796..64b7288fb 100644
--- a/test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go
+++ b/test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go
@@ -71,7 +71,7 @@ func TestSendWindowSizesPiggyback(t *testing.T) {
dut.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
- expectedTCP := testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}
+ expectedTCP := testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}
dut.Send(t, acceptFd, sampleData, 0)
expectedPayload := testbench.Payload{Bytes: tt.expectedPayload1}
@@ -90,7 +90,7 @@ func TestSendWindowSizesPiggyback(t *testing.T) {
// Send ACK for the previous segment along with data for the dut to
// receive and ACK back. Sending this ACK would make room for the dut
// to transmit any enqueued segment.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh), WindowSize: testbench.Uint16(tt.windowSize)}, &testbench.Payload{Bytes: sampleData})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh), WindowSize: testbench.Uint16(tt.windowSize)}, &testbench.Payload{Bytes: sampleData})
// Expect the dut to piggyback the ACK for received data along with
// the segment enqueued for transmit.
diff --git a/test/packetimpact/tests/tcp_synrcvd_reset_test.go b/test/packetimpact/tests/tcp_synrcvd_reset_test.go
index 32271d7b2..3346d43c4 100644
--- a/test/packetimpact/tests/tcp_synrcvd_reset_test.go
+++ b/test/packetimpact/tests/tcp_synrcvd_reset_test.go
@@ -37,11 +37,11 @@ func TestTCPSynRcvdReset(t *testing.T) {
defer conn.Close(t)
// Expect dut connection to have transitioned to SYN-RCVD state.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
t.Fatalf("expected SYN-ACK %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)})
// Expect the connection to have transitioned SYN-RCVD to CLOSED.
//
@@ -49,8 +49,8 @@ func TestTCPSynRcvdReset(t *testing.T) {
// CLOSED. We cannot use TCP_INFO to lookup the state as this is a passive
// DUT connection.
for i := 0; i < 5; i++ {
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, nil, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, nil, time.Second); err != nil {
t.Logf("retransmit%d ACK as we did not get the expected RST, %s", i, err)
continue
}
diff --git a/test/packetimpact/tests/tcp_synsent_reset_test.go b/test/packetimpact/tests/tcp_synsent_reset_test.go
index 2c8bb101b..cccb0abc6 100644
--- a/test/packetimpact/tests/tcp_synsent_reset_test.go
+++ b/test/packetimpact/tests/tcp_synsent_reset_test.go
@@ -42,7 +42,7 @@ func dutSynSentState(t *testing.T) (*testbench.DUT, *testbench.TCPIPv4, uint16,
copy(sa.Addr[:], dut.Net.LocalIPv4)
// Bring the dut to SYN-SENT state with a non-blocking connect.
dut.Connect(t, clientFD, &sa)
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)}, nil, time.Second); err != nil {
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, nil, time.Second); err != nil {
t.Fatalf("expected SYN\n")
}
@@ -53,11 +53,11 @@ func dutSynSentState(t *testing.T) (*testbench.DUT, *testbench.TCPIPv4, uint16,
func TestTCPSynSentReset(t *testing.T) {
_, conn, _, _ := dutSynSentState(t)
defer conn.Close(t)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)})
// Expect the connection to have closed.
// TODO(gvisor.dev/issue/478): Check for TCP_INFO on the dut side.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, nil, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, nil, time.Second); err != nil {
t.Fatalf("expected a TCP RST")
}
}
@@ -73,15 +73,15 @@ func TestTCPSynSentRcvdReset(t *testing.T) {
// Initiate new SYN connection with the same port pair
// (simultaneous open case), expect the dut connection to move to
// SYN-RCVD state
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
t.Fatalf("expected SYN-ACK %s\n", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)})
// Expect the connection to have transitioned SYN-RCVD to CLOSED.
// TODO(gvisor.dev/issue/478): Check for TCP_INFO on the dut side.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, nil, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, nil, time.Second); err != nil {
t.Fatalf("expected a TCP RST")
}
}
diff --git a/test/packetimpact/tests/tcp_timewait_reset_test.go b/test/packetimpact/tests/tcp_timewait_reset_test.go
index d1d2fb83d..89037f0a4 100644
--- a/test/packetimpact/tests/tcp_timewait_reset_test.go
+++ b/test/packetimpact/tests/tcp_timewait_reset_test.go
@@ -42,26 +42,26 @@ func TestTimeWaitReset(t *testing.T) {
// Trigger active close.
dut.Close(t, acceptFD)
- _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second)
+ _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("expected a FIN: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
// Send a FIN, DUT should transition to TIME_WAIT from FIN_WAIT2.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
t.Fatalf("expected an ACK for our FIN: %s", err)
}
// Send a RST, the DUT should transition to CLOSED from TIME_WAIT.
// This is the default Linux behavior, it can be changed to ignore RSTs via
// sysctl net.ipv4.tcp_rfc1337.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)})
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
// The DUT should reply with RST to our ACK as the state should have
// transitioned to CLOSED.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, time.Second); err != nil {
t.Fatalf("expected a RST: %s", err)
}
}
diff --git a/test/packetimpact/tests/tcp_unacc_seq_ack_closing_test.go b/test/packetimpact/tests/tcp_unacc_seq_ack_closing_test.go
new file mode 100644
index 000000000..a208210ac
--- /dev/null
+++ b/test/packetimpact/tests/tcp_unacc_seq_ack_closing_test.go
@@ -0,0 +1,94 @@
+// Copyright 2021 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 tcp_unacc_seq_ack_closing_test
+
+import (
+ "flag"
+ "fmt"
+ "testing"
+ "time"
+
+ "golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/tcpip/header"
+ "gvisor.dev/gvisor/pkg/tcpip/seqnum"
+ "gvisor.dev/gvisor/test/packetimpact/testbench"
+)
+
+func init() {
+ testbench.Initialize(flag.CommandLine)
+}
+
+func TestSimultaneousCloseUnaccSeqAck(t *testing.T) {
+ for _, tt := range []struct {
+ description string
+ makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
+ seqNumOffset seqnum.Size
+ expectAck bool
+ }{
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, expectAck: true},
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, expectAck: true},
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, expectAck: true},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, expectAck: false},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, expectAck: true},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, expectAck: true},
+ } {
+ t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
+ dut := testbench.NewDUT(t)
+ listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
+ defer dut.Close(t, listenFD)
+ conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ defer conn.Close(t)
+
+ conn.Connect(t)
+ acceptFD, _ := dut.Accept(t, listenFD)
+
+ // Trigger active close.
+ dut.Shutdown(t, acceptFD, unix.SHUT_WR)
+
+ gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second)
+ if err != nil {
+ t.Fatalf("expected a FIN: %s", err)
+ }
+ // Do not ack the FIN from DUT so that we get to CLOSING.
+ seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1)
+ conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
+
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
+ t.Errorf("expected an ACK to our FIN, but got none: %s", err)
+ }
+
+ sampleData := []byte("Sample Data")
+ samplePayload := &testbench.Payload{Bytes: sampleData}
+
+ origSeq := uint32(*conn.LocalSeqNum(t))
+ // Send a segment with OTW Seq / unacc ACK.
+ tcp := tt.makeTestingTCP(t, &conn, tt.seqNumOffset, seqnum.Size(*gotTCP.WindowSize))
+ if tt.description == "OTWSeq" {
+ // If we generate an OTW Seq segment, make sure we don't acknowledge their FIN so that
+ // we stay in CLOSING.
+ tcp.AckNum = seqNumForTheirFIN
+ }
+ conn.Send(t, tcp, samplePayload)
+
+ got, err := conn.Expect(t, testbench.TCP{AckNum: testbench.Uint32(origSeq), Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
+ if tt.expectAck && err != nil {
+ t.Errorf("expected an ack in CLOSING state, but got none: %s", err)
+ }
+ if !tt.expectAck && got != nil {
+ t.Errorf("expected no ack in CLOSING state, but got one: %s", got)
+ }
+ })
+ }
+}
diff --git a/test/packetimpact/tests/tcp_unacc_seq_ack_test.go b/test/packetimpact/tests/tcp_unacc_seq_ack_test.go
index ea962c818..ce0a26171 100644
--- a/test/packetimpact/tests/tcp_unacc_seq_ack_test.go
+++ b/test/packetimpact/tests/tcp_unacc_seq_ack_test.go
@@ -17,7 +17,6 @@ package tcp_unacc_seq_ack_test
import (
"flag"
"fmt"
- "syscall"
"testing"
"time"
@@ -39,12 +38,12 @@ func TestEstablishedUnaccSeqAck(t *testing.T) {
expectAck bool
restoreSeq bool
}{
- {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 0, expectAck: true, restoreSeq: true},
- {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 1, expectAck: true, restoreSeq: true},
- {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 2, expectAck: true, restoreSeq: true},
- {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 0, expectAck: true, restoreSeq: false},
- {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 1, expectAck: false, restoreSeq: true},
- {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 2, expectAck: false, restoreSeq: true},
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, expectAck: true, restoreSeq: true},
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, expectAck: true, restoreSeq: true},
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, expectAck: true, restoreSeq: true},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, expectAck: true, restoreSeq: false},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, expectAck: false, restoreSeq: true},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, expectAck: false, restoreSeq: true},
} {
t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
dut := testbench.NewDUT(t)
@@ -59,8 +58,8 @@ func TestEstablishedUnaccSeqAck(t *testing.T) {
sampleData := []byte("Sample Data")
samplePayload := &testbench.Payload{Bytes: sampleData}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
+ gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("expected ack %s", err)
}
@@ -74,7 +73,7 @@ func TestEstablishedUnaccSeqAck(t *testing.T) {
// ACK matches the TCP layer state.
*conn.LocalSeqNum(t) = origSeq
}
- gotAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ gotAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if tt.expectAck && err != nil {
t.Fatalf("expected an ack but got none: %s", err)
}
@@ -92,12 +91,12 @@ func TestPassiveCloseUnaccSeqAck(t *testing.T) {
seqNumOffset seqnum.Size
expectAck bool
}{
- {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 0, expectAck: false},
- {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 1, expectAck: true},
- {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 2, expectAck: true},
- {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 0, expectAck: false},
- {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 1, expectAck: true},
- {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 2, expectAck: true},
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, expectAck: false},
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, expectAck: true},
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, expectAck: true},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, expectAck: false},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, expectAck: true},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, expectAck: true},
} {
t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
dut := testbench.NewDUT(t)
@@ -110,8 +109,8 @@ func TestPassiveCloseUnaccSeqAck(t *testing.T) {
acceptFD, _ := dut.Accept(t, listenFD)
// Send a FIN to DUT to intiate the passive close.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagFin)})
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagFin)})
+ gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("expected an ACK for our fin and DUT should enter CLOSE_WAIT: %s", err)
}
@@ -122,7 +121,7 @@ func TestPassiveCloseUnaccSeqAck(t *testing.T) {
// Send a segment with OTW Seq / unacc ACK.
conn.Send(t, tt.makeTestingTCP(t, &conn, tt.seqNumOffset, windowSize), samplePayload)
- gotAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ gotAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if tt.expectAck && err != nil {
t.Errorf("expected an ack but got none: %s", err)
}
@@ -132,14 +131,14 @@ func TestPassiveCloseUnaccSeqAck(t *testing.T) {
// Now let's verify DUT is indeed in CLOSE_WAIT
dut.Close(t, acceptFD)
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagFin)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagFin)}, time.Second); err != nil {
t.Fatalf("expected DUT to send a FIN: %s", err)
}
// Ack the FIN from DUT
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
// Send some extra data to DUT
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, samplePayload)
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, samplePayload)
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, time.Second); err != nil {
t.Fatalf("expected DUT to send an RST: %s", err)
}
})
@@ -153,12 +152,12 @@ func TestActiveCloseUnaccpSeqAck(t *testing.T) {
seqNumOffset seqnum.Size
restoreSeq bool
}{
- {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 0, restoreSeq: true},
- {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 1, restoreSeq: true},
- {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 2, restoreSeq: true},
- {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 0, restoreSeq: false},
- {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 1, restoreSeq: true},
- {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 2, restoreSeq: true},
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, restoreSeq: true},
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, restoreSeq: true},
+ {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, restoreSeq: true},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, restoreSeq: false},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, restoreSeq: true},
+ {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, restoreSeq: true},
} {
t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
dut := testbench.NewDUT(t)
@@ -171,14 +170,14 @@ func TestActiveCloseUnaccpSeqAck(t *testing.T) {
acceptFD, _ := dut.Accept(t, listenFD)
// Trigger active close.
- dut.Shutdown(t, acceptFD, syscall.SHUT_WR)
+ dut.Shutdown(t, acceptFD, unix.SHUT_WR)
// Get to FIN_WAIT2
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second)
+ gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("expected a FIN: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
sendUnaccSeqAck := func(state string) {
t.Helper()
@@ -193,7 +192,7 @@ func TestActiveCloseUnaccpSeqAck(t *testing.T) {
// incoming ACK matches the TCP layer state.
*conn.LocalSeqNum(t) = origSeq
}
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
t.Errorf("expected an ack in %s state, but got none: %s", state, err)
}
}
@@ -201,8 +200,8 @@ func TestActiveCloseUnaccpSeqAck(t *testing.T) {
sendUnaccSeqAck("FIN_WAIT2")
// Send a FIN to DUT to get to TIME_WAIT
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
t.Fatalf("expected an ACK for our fin and DUT should enter TIME_WAIT: %s", err)
}
@@ -210,22 +209,3 @@ func TestActiveCloseUnaccpSeqAck(t *testing.T) {
})
}
}
-
-// generateOTWSeqSegment generates an segment with
-// seqnum = RCV.NXT + RCV.WND + seqNumOffset, the generated segment is only
-// acceptable when seqNumOffset is 0, otherwise an ACK is expected from the
-// receiver.
-func generateOTWSeqSegment(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP {
- lastAcceptable := conn.LocalSeqNum(t).Add(windowSize)
- otwSeq := uint32(lastAcceptable.Add(seqNumOffset))
- return testbench.TCP{SeqNum: testbench.Uint32(otwSeq), Flags: testbench.Uint8(header.TCPFlagAck)}
-}
-
-// generateUnaccACKSegment generates an segment with
-// acknum = SND.NXT + seqNumOffset, the generated segment is only acceptable
-// when seqNumOffset is 0, otherwise an ACK is expected from the receiver.
-func generateUnaccACKSegment(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP {
- lastAcceptable := conn.RemoteSeqNum(t)
- unaccAck := uint32(lastAcceptable.Add(seqNumOffset))
- return testbench.TCP{AckNum: testbench.Uint32(unaccAck), Flags: testbench.Uint8(header.TCPFlagAck)}
-}
diff --git a/test/packetimpact/tests/tcp_user_timeout_test.go b/test/packetimpact/tests/tcp_user_timeout_test.go
index b16e65366..ef38bd738 100644
--- a/test/packetimpact/tests/tcp_user_timeout_test.go
+++ b/test/packetimpact/tests/tcp_user_timeout_test.go
@@ -35,7 +35,7 @@ func sendPayload(t *testing.T, conn *testbench.TCPIPv4, dut *testbench.DUT, fd i
}
conn.Drain(t)
dut.Send(t, fd, sampleData, 0)
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &testbench.Payload{Bytes: sampleData}, time.Second); err != nil {
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, &testbench.Payload{Bytes: sampleData}, time.Second); err != nil {
t.Fatalf("expected data but got none: %w", err)
}
}
@@ -79,14 +79,14 @@ func TestTCPUserTimeout(t *testing.T) {
time.Sleep(tt.sendDelay)
conn.Drain(t)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
// If TCP_USER_TIMEOUT was set and the above delay was longer than the
// TCP_USER_TIMEOUT then the DUT should send a RST in response to the
// testbench's packet.
expectRST := tt.userTimeout != 0 && tt.sendDelay > tt.userTimeout
expectTimeout := 5 * time.Second
- got, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, expectTimeout)
+ got, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, expectTimeout)
if expectRST && err != nil {
t.Errorf("expected RST packet within %s but got none: %s", expectTimeout, err)
}
diff --git a/test/packetimpact/tests/tcp_window_shrink_test.go b/test/packetimpact/tests/tcp_window_shrink_test.go
index 093484721..0d65a2ea2 100644
--- a/test/packetimpact/tests/tcp_window_shrink_test.go
+++ b/test/packetimpact/tests/tcp_window_shrink_test.go
@@ -48,7 +48,7 @@ func TestWindowShrink(t *testing.T) {
if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
dut.Send(t, acceptFd, sampleData, 0)
dut.Send(t, acceptFd, sampleData, 0)
@@ -59,7 +59,7 @@ func TestWindowShrink(t *testing.T) {
t.Fatalf("expected payload was not received: %s", err)
}
// We close our receiving window here
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
dut.Send(t, acceptFd, []byte("Sample Data"), 0)
// Note: There is another kind of zero-window probing which Windows uses (by sending one
diff --git a/test/packetimpact/tests/tcp_zero_receive_window_test.go b/test/packetimpact/tests/tcp_zero_receive_window_test.go
index d06690705..d73495454 100644
--- a/test/packetimpact/tests/tcp_zero_receive_window_test.go
+++ b/test/packetimpact/tests/tcp_zero_receive_window_test.go
@@ -49,8 +49,8 @@ func TestZeroReceiveWindow(t *testing.T) {
// Expect the DUT to eventually advertise zero receive window.
// The test would timeout otherwise.
for readOnce := false; ; {
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
+ gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("expected packet was not received: %s", err)
}
@@ -100,8 +100,8 @@ func TestNonZeroReceiveWindow(t *testing.T) {
// we sent. Once we have received ACKs with non-zero receive windows, we break
// the loop.
for {
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
+ gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("expected packet was not received: %s", err)
}
diff --git a/test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go b/test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go
index d094c10eb..22b17a39e 100644
--- a/test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go
+++ b/test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go
@@ -15,6 +15,7 @@
package tcp_zero_window_probe_retransmit_test
import (
+ "bytes"
"flag"
"testing"
"time"
@@ -51,18 +52,23 @@ func TestZeroWindowProbeRetransmit(t *testing.T) {
if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
// Check for the dut to keep the connection alive as long as the zero window
// probes are acknowledged. Check if the zero window probes are sent at
// exponentially increasing intervals. The timeout intervals are function
// of the recorded first zero probe transmission duration.
//
- // Advertize zero receive window again.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
+ // Advertize zero receive window along with a payload.
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh), WindowSize: testbench.Uint16(0)}, samplePayload)
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, nil, time.Second); err != nil {
+ t.Fatalf("expected packet was not received: %s", err)
+ }
+ // Wait for the payload to be received by the DUT, which is also an
+ // indication of receive of the peer window advertisement.
+ if got := dut.Recv(t, acceptFd, int32(len(sampleData)), 0); !bytes.Equal(got, sampleData) {
+ t.Fatalf("got dut.Recv(t, %d, %d, 0) = %s, want %s", acceptFd, len(sampleData), got, sampleData)
+ }
+
probeSeq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t) - 1))
ackProbe := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)))
@@ -98,10 +104,10 @@ func TestZeroWindowProbeRetransmit(t *testing.T) {
}
prev = got
// Acknowledge the zero-window probes from the dut.
- conn.Send(t, testbench.TCP{AckNum: ackProbe, Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
+ conn.Send(t, testbench.TCP{AckNum: ackProbe, Flags: testbench.TCPFlags(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
}
// Advertize non-zero window.
- conn.Send(t, testbench.TCP{AckNum: ackProbe, Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{AckNum: ackProbe, Flags: testbench.TCPFlags(header.TCPFlagAck)})
// Expect the dut to recover and transmit data.
if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: ackProbe}, samplePayload, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
diff --git a/test/packetimpact/tests/tcp_zero_window_probe_test.go b/test/packetimpact/tests/tcp_zero_window_probe_test.go
index 650a569cc..8b90fcbe9 100644
--- a/test/packetimpact/tests/tcp_zero_window_probe_test.go
+++ b/test/packetimpact/tests/tcp_zero_window_probe_test.go
@@ -53,8 +53,8 @@ func TestZeroWindowProbe(t *testing.T) {
t.Fatalf("expected payload was not received: %s", err)
}
sendTime := time.Now().Sub(start)
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, nil, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, nil, time.Second); err != nil {
t.Fatalf("expected packet was not received: %s", err)
}
@@ -62,7 +62,7 @@ func TestZeroWindowProbe(t *testing.T) {
// probe to be sent.
//
// Advertize zero window to the dut.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
// Expected sequence number of the zero window probe.
probeSeq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t) - 1))
@@ -93,7 +93,7 @@ func TestZeroWindowProbe(t *testing.T) {
// and sends out the sample payload after the send window opens.
//
// Advertize non-zero window to the dut and ack the zero window probe.
- conn.Send(t, testbench.TCP{AckNum: ackProbe, Flags: testbench.Uint8(header.TCPFlagAck)})
+ conn.Send(t, testbench.TCP{AckNum: ackProbe, Flags: testbench.TCPFlags(header.TCPFlagAck)})
// Expect the dut to recover and transmit data.
if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: ackProbe}, samplePayload, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
@@ -104,8 +104,8 @@ func TestZeroWindowProbe(t *testing.T) {
// Basically with sequence number to one byte behind the unacknowledged
// sequence number.
p := testbench.Uint32(uint32(*conn.LocalSeqNum(t)))
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), SeqNum: testbench.Uint32(uint32(*conn.LocalSeqNum(t) - 1))})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: p}, nil, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), SeqNum: testbench.Uint32(uint32(*conn.LocalSeqNum(t) - 1))})
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: p}, nil, time.Second); err != nil {
t.Fatalf("expected a packet with ack number: %d: %s", p, err)
}
}
diff --git a/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go b/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go
index 079fea68c..1ce4d22b7 100644
--- a/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go
+++ b/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go
@@ -51,8 +51,8 @@ func TestZeroWindowProbeUserTimeout(t *testing.T) {
if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, nil, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, nil, time.Second); err != nil {
t.Fatalf("expected packet was not received: %s", err)
}
@@ -60,7 +60,7 @@ func TestZeroWindowProbeUserTimeout(t *testing.T) {
// probe to be sent.
//
// Advertize zero window to the dut.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
// Expected sequence number of the zero window probe.
probeSeq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t) - 1))
@@ -81,7 +81,7 @@ func TestZeroWindowProbeUserTimeout(t *testing.T) {
// Reduce the retransmit timeout.
dut.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, int32(startProbeDuration.Milliseconds()))
// Advertize zero window again.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
// Ask the dut to send out data that would trigger zero window probe retransmissions.
dut.Send(t, acceptFd, sampleData, 0)
@@ -90,8 +90,8 @@ func TestZeroWindowProbeUserTimeout(t *testing.T) {
// Expect the connection to have timed out and closed which would cause the dut
// to reply with a RST to the ACK we send.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, nil, time.Second); err != nil {
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
+ if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, nil, time.Second); err != nil {
t.Fatalf("expected a TCP RST")
}
}
diff --git a/test/packetimpact/tests/udp_discard_mcast_source_addr_test.go b/test/packetimpact/tests/udp_discard_mcast_source_addr_test.go
index 52c6f9d91..f63cfcc9a 100644
--- a/test/packetimpact/tests/udp_discard_mcast_source_addr_test.go
+++ b/test/packetimpact/tests/udp_discard_mcast_source_addr_test.go
@@ -19,7 +19,6 @@ import (
"flag"
"fmt"
"net"
- "syscall"
"testing"
"golang.org/x/sys/unix"
@@ -56,7 +55,7 @@ func TestDiscardsUDPPacketsWithMcastSourceAddressV4(t *testing.T) {
)
ret, payload, errno := dut.RecvWithErrno(context.Background(), t, remoteFD, 100, 0)
- if errno != syscall.EAGAIN || errno != syscall.EWOULDBLOCK {
+ if errno != unix.EAGAIN || errno != unix.EWOULDBLOCK {
t.Errorf("Recv got unexpected result, ret=%d, payload=%q, errno=%s", ret, payload, errno)
}
})
@@ -86,7 +85,7 @@ func TestDiscardsUDPPacketsWithMcastSourceAddressV6(t *testing.T) {
&testbench.Payload{Bytes: []byte("test payload")},
)
ret, payload, errno := dut.RecvWithErrno(context.Background(), t, remoteFD, 100, 0)
- if errno != syscall.EAGAIN || errno != syscall.EWOULDBLOCK {
+ if errno != unix.EAGAIN || errno != unix.EWOULDBLOCK {
t.Errorf("Recv got unexpected result, ret=%d, payload=%q, errno=%s", ret, payload, errno)
}
})
diff --git a/test/packetimpact/tests/udp_icmp_error_propagation_test.go b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
index 3fca8c7a3..3159d5b89 100644
--- a/test/packetimpact/tests/udp_icmp_error_propagation_test.go
+++ b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
@@ -20,7 +20,6 @@ import (
"fmt"
"net"
"sync"
- "syscall"
"testing"
"time"
@@ -86,16 +85,16 @@ type testData struct {
remotePort uint16
cleanFD int32
cleanPort uint16
- wantErrno syscall.Errno
+ wantErrno unix.Errno
}
// wantErrno computes the errno to expect given the connection mode of a UDP
// socket and the ICMP error it will receive.
-func wantErrno(c connectionMode, icmpErr icmpError) syscall.Errno {
+func wantErrno(c connectionMode, icmpErr icmpError) unix.Errno {
if c && icmpErr == portUnreachable {
- return syscall.Errno(unix.ECONNREFUSED)
+ return unix.ECONNREFUSED
}
- return syscall.Errno(0)
+ return unix.Errno(0)
}
// sendICMPError sends an ICMP error message in response to a UDP datagram.
@@ -123,7 +122,7 @@ func sendICMPError(t *testing.T, conn *testbench.UDPIPv4, icmpErr icmpError, udp
conn.SendFrameStateless(t, layers)
}
-// testRecv tests observing the ICMP error through the recv syscall. A packet
+// testRecv tests observing the ICMP error through the recv unix. A packet
// is sent to the DUT, and if wantErrno is non-zero, then the first recv should
// fail and the second should succeed. Otherwise if wantErrno is zero then the
// first recv should succeed immediately.
@@ -136,7 +135,7 @@ func testRecv(ctx context.Context, t *testing.T, d testData) {
d.conn.Send(t, testbench.UDP{})
- if d.wantErrno != syscall.Errno(0) {
+ if d.wantErrno != unix.Errno(0) {
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
ret, _, err := d.dut.RecvWithErrno(ctx, t, d.remoteFD, 100, 0)
@@ -162,7 +161,7 @@ func testSendTo(ctx context.Context, t *testing.T, d testData) {
t.Fatalf("did not receive UDP packet from clean socket on DUT: %s", err)
}
- if d.wantErrno != syscall.Errno(0) {
+ if d.wantErrno != unix.Errno(0) {
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
ret, err := d.dut.SendToWithErrno(ctx, t, d.remoteFD, nil, 0, d.conn.LocalAddr(t))
@@ -183,11 +182,11 @@ func testSendTo(ctx context.Context, t *testing.T, d testData) {
func testSockOpt(_ context.Context, t *testing.T, d testData) {
// Check that there's no pending error on the clean socket.
- if errno := syscall.Errno(d.dut.GetSockOptInt(t, d.cleanFD, unix.SOL_SOCKET, unix.SO_ERROR)); errno != syscall.Errno(0) {
+ if errno := unix.Errno(d.dut.GetSockOptInt(t, d.cleanFD, unix.SOL_SOCKET, unix.SO_ERROR)); errno != unix.Errno(0) {
t.Fatalf("unexpected error (%[1]d) %[1]v on clean socket", errno)
}
- if errno := syscall.Errno(d.dut.GetSockOptInt(t, d.remoteFD, unix.SOL_SOCKET, unix.SO_ERROR)); errno != d.wantErrno {
+ if errno := unix.Errno(d.dut.GetSockOptInt(t, d.remoteFD, unix.SOL_SOCKET, unix.SO_ERROR)); errno != d.wantErrno {
t.Fatalf("SO_ERROR sockopt after ICMP error is (%[1]d) %[1]v, expected (%[2]d) %[2]v", errno, d.wantErrno)
}
@@ -310,7 +309,7 @@ func TestICMPErrorDuringUDPRecv(t *testing.T) {
go func() {
defer wg.Done()
- if wantErrno != syscall.Errno(0) {
+ if wantErrno != unix.Errno(0) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
diff --git a/test/packetimpact/tests/udp_send_recv_dgram_test.go b/test/packetimpact/tests/udp_send_recv_dgram_test.go
index 894d156cf..230b012c7 100644
--- a/test/packetimpact/tests/udp_send_recv_dgram_test.go
+++ b/test/packetimpact/tests/udp_send_recv_dgram_test.go
@@ -19,7 +19,6 @@ import (
"flag"
"fmt"
"net"
- "syscall"
"testing"
"time"
@@ -241,7 +240,7 @@ func TestUDP(t *testing.T) {
},
)
ret, recvPayload, errno := dut.RecvWithErrno(context.Background(), t, socketFD, 100, 0)
- if errno != syscall.EAGAIN || errno != syscall.EWOULDBLOCK {
+ if errno != unix.EAGAIN || errno != unix.EWOULDBLOCK {
t.Errorf("Recv got unexpected result, ret=%d, payload=%q, errno=%s", ret, recvPayload, errno)
}
}
diff --git a/test/perf/BUILD b/test/perf/BUILD
index e25f090ae..ed899ac22 100644
--- a/test/perf/BUILD
+++ b/test/perf/BUILD
@@ -1,4 +1,3 @@
-load("//tools:defs.bzl", "more_shards")
load("//test/runner:defs.bzl", "syscall_test")
package(licenses = ["notice"])
@@ -38,7 +37,6 @@ syscall_test(
syscall_test(
size = "enormous",
debug = False,
- shard_count = more_shards,
tags = ["nogotsan"],
test = "//test/perf/linux:getdents_benchmark",
)
diff --git a/test/runner/gtest/gtest.go b/test/runner/gtest/gtest.go
index 38e57d62f..2ad5f58ef 100644
--- a/test/runner/gtest/gtest.go
+++ b/test/runner/gtest/gtest.go
@@ -35,6 +35,39 @@ var (
filterBenchmarkFlag = "--benchmark_filter"
)
+// BuildTestArgs builds arguments to be passed to the test binary to execute
+// only the test cases in `indices`.
+func BuildTestArgs(indices []int, testCases []TestCase) []string {
+ var testFilter, benchFilter string
+ for _, tci := range indices {
+ tc := testCases[tci]
+ if tc.all {
+ // No argument will make all tests run.
+ return nil
+ }
+ if tc.benchmark {
+ if len(benchFilter) > 0 {
+ benchFilter += "|"
+ }
+ benchFilter += "^" + tc.Name + "$"
+ } else {
+ if len(testFilter) > 0 {
+ testFilter += ":"
+ }
+ testFilter += tc.FullName()
+ }
+ }
+
+ var args []string
+ if len(testFilter) > 0 {
+ args = append(args, fmt.Sprintf("%s=%s", filterTestFlag, testFilter))
+ }
+ if len(benchFilter) > 0 {
+ args = append(args, fmt.Sprintf("%s=%s", filterBenchmarkFlag, benchFilter))
+ }
+ return args
+}
+
// TestCase is a single gtest test case.
type TestCase struct {
// Suite is the suite for this test.
@@ -59,22 +92,6 @@ func (tc TestCase) FullName() string {
return fmt.Sprintf("%s.%s", tc.Suite, tc.Name)
}
-// Args returns arguments to be passed when invoking the test.
-func (tc TestCase) Args() []string {
- if tc.all {
- return []string{} // No arguments.
- }
- if tc.benchmark {
- return []string{
- fmt.Sprintf("%s=^%s$", filterBenchmarkFlag, tc.Name),
- fmt.Sprintf("%s=", filterTestFlag),
- }
- }
- return []string{
- fmt.Sprintf("%s=%s", filterTestFlag, tc.FullName()),
- }
-}
-
// ParseTestCases calls a gtest test binary to list its test and returns a
// slice with the name and suite of each test.
//
@@ -90,6 +107,7 @@ func ParseTestCases(testBin string, benchmarks bool, extraArgs ...string) ([]Tes
// We failed to list tests with the given flags. Just
// return something that will run the binary with no
// flags, which should execute all tests.
+ fmt.Printf("failed to get test list: %v\n", err)
return []TestCase{
{
Suite: "Default",
diff --git a/test/runner/runner.go b/test/runner/runner.go
index e72c59200..a8a134fe2 100644
--- a/test/runner/runner.go
+++ b/test/runner/runner.go
@@ -26,7 +26,6 @@ import (
"path/filepath"
"strings"
"syscall"
- "testing"
"time"
specs "github.com/opencontainers/runtime-spec/specs-go"
@@ -57,13 +56,82 @@ var (
leakCheck = flag.Bool("leak-check", false, "check for reference leaks")
)
+func main() {
+ flag.Parse()
+ if flag.NArg() != 1 {
+ fatalf("test must be provided")
+ }
+
+ log.SetLevel(log.Info)
+ if *debug {
+ log.SetLevel(log.Debug)
+ }
+
+ if *platform != "native" && *runscPath == "" {
+ if err := testutil.ConfigureExePath(); err != nil {
+ panic(err.Error())
+ }
+ *runscPath = specutils.ExePath
+ }
+
+ // Make sure stdout and stderr are opened with O_APPEND, otherwise logs
+ // from outside the sandbox can (and will) stomp on logs from inside
+ // the sandbox.
+ for _, f := range []*os.File{os.Stdout, os.Stderr} {
+ flags, err := unix.FcntlInt(f.Fd(), unix.F_GETFL, 0)
+ if err != nil {
+ fatalf("error getting file flags for %v: %v", f, err)
+ }
+ if flags&unix.O_APPEND == 0 {
+ flags |= unix.O_APPEND
+ if _, err := unix.FcntlInt(f.Fd(), unix.F_SETFL, flags); err != nil {
+ fatalf("error setting file flags for %v: %v", f, err)
+ }
+ }
+ }
+
+ // Resolve the absolute path for the binary.
+ testBin, err := filepath.Abs(flag.Args()[0])
+ if err != nil {
+ fatalf("Abs(%q) failed: %v", flag.Args()[0], err)
+ }
+
+ // Get all test cases in each binary.
+ testCases, err := gtest.ParseTestCases(testBin, true)
+ if err != nil {
+ fatalf("ParseTestCases(%q) failed: %v", testBin, err)
+ }
+
+ // Get subset of tests corresponding to shard.
+ indices, err := testutil.TestIndicesForShard(len(testCases))
+ if err != nil {
+ fatalf("TestsForShard() failed: %v", err)
+ }
+ if len(indices) == 0 {
+ log.Warningf("No tests to run in this shard")
+ return
+ }
+ args := gtest.BuildTestArgs(indices, testCases)
+
+ switch *platform {
+ case "native":
+ if err := runTestCaseNative(testBin, args); err != nil {
+ fatalf(err.Error())
+ }
+ default:
+ if err := runTestCaseRunsc(testBin, args); err != nil {
+ fatalf(err.Error())
+ }
+ }
+}
+
// runTestCaseNative runs the test case directly on the host machine.
-func runTestCaseNative(testBin string, tc gtest.TestCase, t *testing.T) {
+func runTestCaseNative(testBin string, args []string) error {
// These tests might be running in parallel, so make sure they have a
// unique test temp dir.
tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "")
if err != nil {
- t.Fatalf("could not create temp dir: %v", err)
+ return fmt.Errorf("could not create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
@@ -84,12 +152,12 @@ func runTestCaseNative(testBin string, tc gtest.TestCase, t *testing.T) {
}
// Remove shard env variables so that the gunit binary does not try to
// interpret them.
- env = filterEnv(env, []string{"TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS", "GTEST_SHARD_INDEX", "GTEST_TOTAL_SHARDS"})
+ env = filterEnv(env, "TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS", "GTEST_SHARD_INDEX", "GTEST_TOTAL_SHARDS")
if *addUDSTree {
socketDir, cleanup, err := uds.CreateSocketTree("/tmp")
if err != nil {
- t.Fatalf("failed to create socket tree: %v", err)
+ return fmt.Errorf("failed to create socket tree: %v", err)
}
defer cleanup()
@@ -99,24 +167,25 @@ func runTestCaseNative(testBin string, tc gtest.TestCase, t *testing.T) {
env = append(env, "TEST_UDS_ATTACH_TREE="+socketDir)
}
- cmd := exec.Command(testBin, tc.Args()...)
+ cmd := exec.Command(testBin, args...)
cmd.Env = env
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
- cmd.SysProcAttr = &syscall.SysProcAttr{}
+ cmd.SysProcAttr = &unix.SysProcAttr{}
if specutils.HasCapabilities(capability.CAP_SYS_ADMIN) {
- cmd.SysProcAttr.Cloneflags |= syscall.CLONE_NEWUTS
+ cmd.SysProcAttr.Cloneflags |= unix.CLONE_NEWUTS
}
if specutils.HasCapabilities(capability.CAP_NET_ADMIN) {
- cmd.SysProcAttr.Cloneflags |= syscall.CLONE_NEWNET
+ cmd.SysProcAttr.Cloneflags |= unix.CLONE_NEWNET
}
if err := cmd.Run(); err != nil {
ws := err.(*exec.ExitError).Sys().(syscall.WaitStatus)
- t.Errorf("test %q exited with status %d, want 0", tc.FullName(), ws.ExitStatus())
+ return fmt.Errorf("test exited with status %d, want 0", ws.ExitStatus())
}
+ return nil
}
// runRunsc runs spec in runsc in a standard test configuration.
@@ -124,7 +193,7 @@ func runTestCaseNative(testBin string, tc gtest.TestCase, t *testing.T) {
// runsc logs will be saved to a path in TEST_UNDECLARED_OUTPUTS_DIR.
//
// Returns an error if the sandboxed application exits non-zero.
-func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
+func runRunsc(spec *specs.Spec) error {
bundleDir, cleanup, err := testutil.SetupBundleDir(spec)
if err != nil {
return fmt.Errorf("SetupBundleDir failed: %v", err)
@@ -137,9 +206,8 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
}
defer cleanup()
- name := tc.FullName()
id := testutil.RandomContainerID()
- log.Infof("Running test %q in container %q", name, id)
+ log.Infof("Running test in container %q", id)
specutils.LogSpec(spec)
args := []string{
@@ -148,7 +216,7 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
"-log-format=text",
"-TESTONLY-unsafe-nonroot=true",
"-net-raw=true",
- fmt.Sprintf("-panic-signal=%d", syscall.SIGTERM),
+ fmt.Sprintf("-panic-signal=%d", unix.SIGTERM),
"-watchdog-action=panic",
"-platform", *platform,
"-file-access", *fileAccess,
@@ -175,13 +243,8 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
args = append(args, "-ref-leak-mode=log-names")
}
- testLogDir := ""
- if undeclaredOutputsDir, ok := syscall.Getenv("TEST_UNDECLARED_OUTPUTS_DIR"); ok {
- // Create log directory dedicated for this test.
- testLogDir = filepath.Join(undeclaredOutputsDir, strings.Replace(name, "/", "_", -1))
- if err := os.MkdirAll(testLogDir, 0755); err != nil {
- return fmt.Errorf("could not create test dir: %v", err)
- }
+ testLogDir := os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR")
+ if len(testLogDir) > 0 {
debugLogDir, err := ioutil.TempDir(testLogDir, "runsc")
if err != nil {
return fmt.Errorf("could not create temp dir: %v", err)
@@ -200,8 +263,8 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
// as root inside that namespace to get it.
rArgs := append(args, "run", "--bundle", bundleDir, id)
cmd := exec.Command(*runscPath, rArgs...)
- cmd.SysProcAttr = &syscall.SysProcAttr{
- Cloneflags: syscall.CLONE_NEWUSER | syscall.CLONE_NEWNS,
+ cmd.SysProcAttr = &unix.SysProcAttr{
+ Cloneflags: unix.CLONE_NEWUSER | unix.CLONE_NEWNS,
// Set current user/group as root inside the namespace.
UidMappings: []syscall.SysProcIDMap{
{ContainerID: 0, HostID: os.Getuid(), Size: 1},
@@ -219,14 +282,14 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
cmd.Stderr = os.Stderr
sig := make(chan os.Signal, 1)
defer close(sig)
- signal.Notify(sig, syscall.SIGTERM)
+ signal.Notify(sig, unix.SIGTERM)
defer signal.Stop(sig)
go func() {
s, ok := <-sig
if !ok {
return
}
- log.Warningf("%s: Got signal: %v", name, s)
+ log.Warningf("Got signal: %v", s)
done := make(chan bool, 1)
dArgs := append([]string{}, args...)
dArgs = append(dArgs, "-alsologtostderr=true", "debug", "--stacks", id)
@@ -247,7 +310,7 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
log.Warningf("Send SIGTERM to the sandbox process")
dArgs = append(args, "debug",
- fmt.Sprintf("--signal=%d", syscall.SIGTERM),
+ fmt.Sprintf("--signal=%d", unix.SIGTERM),
id)
signal := exec.Command(*runscPath, dArgs...)
signal.Stdout = os.Stdout
@@ -259,7 +322,7 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
if err == nil && len(testLogDir) > 0 {
// If the test passed, then we erase the log directory. This speeds up
// uploading logs in continuous integration & saves on disk space.
- os.RemoveAll(testLogDir)
+ _ = os.RemoveAll(testLogDir)
}
return err
@@ -314,10 +377,10 @@ func setupUDSTree(spec *specs.Spec) (cleanup func(), err error) {
}
// runsTestCaseRunsc runs the test case in runsc.
-func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) {
+func runTestCaseRunsc(testBin string, args []string) error {
// Run a new container with the test executable and filter for the
// given test suite and name.
- spec := testutil.NewSpecWithArgs(append([]string{testBin}, tc.Args()...)...)
+ spec := testutil.NewSpecWithArgs(append([]string{testBin}, args...)...)
// Mark the root as writeable, as some tests attempt to
// write to the rootfs, and expect EACCES, not EROFS.
@@ -343,12 +406,12 @@ func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) {
// users, so make sure it is world-accessible.
tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "")
if err != nil {
- t.Fatalf("could not create temp dir: %v", err)
+ return fmt.Errorf("could not create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
if err := os.Chmod(tmpDir, 0777); err != nil {
- t.Fatalf("could not chmod temp dir: %v", err)
+ return fmt.Errorf("could not chmod temp dir: %v", err)
}
// "/tmp" is not replaced with a tmpfs mount inside the sandbox
@@ -368,13 +431,12 @@ func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) {
// Set environment variables that indicate we are running in gVisor with
// the given platform, network, and filesystem stack.
- platformVar := "TEST_ON_GVISOR"
- networkVar := "GVISOR_NETWORK"
- env := append(os.Environ(), platformVar+"="+*platform, networkVar+"="+*network)
- vfsVar := "GVISOR_VFS"
+ env := []string{"TEST_ON_GVISOR=" + *platform, "GVISOR_NETWORK=" + *network}
+ env = append(env, os.Environ()...)
+ const vfsVar = "GVISOR_VFS"
if *vfs2 {
env = append(env, vfsVar+"=VFS2")
- fuseVar := "FUSE_ENABLED"
+ const fuseVar = "FUSE_ENABLED"
if *fuse {
env = append(env, fuseVar+"=TRUE")
} else {
@@ -386,11 +448,11 @@ func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) {
// Remove shard env variables so that the gunit binary does not try to
// interpret them.
- env = filterEnv(env, []string{"TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS", "GTEST_SHARD_INDEX", "GTEST_TOTAL_SHARDS"})
+ env = filterEnv(env, "TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS", "GTEST_SHARD_INDEX", "GTEST_TOTAL_SHARDS")
// Set TEST_TMPDIR to /tmp, as some of the syscall tests require it to
// be backed by tmpfs.
- env = filterEnv(env, []string{"TEST_TMPDIR"})
+ env = filterEnv(env, "TEST_TMPDIR")
env = append(env, fmt.Sprintf("TEST_TMPDIR=%s", testTmpDir))
spec.Process.Env = env
@@ -398,18 +460,19 @@ func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) {
if *addUDSTree {
cleanup, err := setupUDSTree(spec)
if err != nil {
- t.Fatalf("error creating UDS tree: %v", err)
+ return fmt.Errorf("error creating UDS tree: %v", err)
}
defer cleanup()
}
- if err := runRunsc(tc, spec); err != nil {
- t.Errorf("test %q failed with error %v, want nil", tc.FullName(), err)
+ if err := runRunsc(spec); err != nil {
+ return fmt.Errorf("test failed with error %v, want nil", err)
}
+ return nil
}
// filterEnv returns an environment with the excluded variables removed.
-func filterEnv(env, exclude []string) []string {
+func filterEnv(env []string, exclude ...string) []string {
var out []string
for _, kv := range env {
ok := true
@@ -430,82 +493,3 @@ func fatalf(s string, args ...interface{}) {
fmt.Fprintf(os.Stderr, s+"\n", args...)
os.Exit(1)
}
-
-func matchString(a, b string) (bool, error) {
- return a == b, nil
-}
-
-func main() {
- flag.Parse()
- if flag.NArg() != 1 {
- fatalf("test must be provided")
- }
- testBin := flag.Args()[0] // Only argument.
-
- log.SetLevel(log.Info)
- if *debug {
- log.SetLevel(log.Debug)
- }
-
- if *platform != "native" && *runscPath == "" {
- if err := testutil.ConfigureExePath(); err != nil {
- panic(err.Error())
- }
- *runscPath = specutils.ExePath
- }
-
- // Make sure stdout and stderr are opened with O_APPEND, otherwise logs
- // from outside the sandbox can (and will) stomp on logs from inside
- // the sandbox.
- for _, f := range []*os.File{os.Stdout, os.Stderr} {
- flags, err := unix.FcntlInt(f.Fd(), unix.F_GETFL, 0)
- if err != nil {
- fatalf("error getting file flags for %v: %v", f, err)
- }
- if flags&unix.O_APPEND == 0 {
- flags |= unix.O_APPEND
- if _, err := unix.FcntlInt(f.Fd(), unix.F_SETFL, flags); err != nil {
- fatalf("error setting file flags for %v: %v", f, err)
- }
- }
- }
-
- // Get all test cases in each binary.
- testCases, err := gtest.ParseTestCases(testBin, true)
- if err != nil {
- fatalf("ParseTestCases(%q) failed: %v", testBin, err)
- }
-
- // Get subset of tests corresponding to shard.
- indices, err := testutil.TestIndicesForShard(len(testCases))
- if err != nil {
- fatalf("TestsForShard() failed: %v", err)
- }
-
- // Resolve the absolute path for the binary.
- testBin, err = filepath.Abs(testBin)
- if err != nil {
- fatalf("Abs() failed: %v", err)
- }
-
- // Run the tests.
- var tests []testing.InternalTest
- for _, tci := range indices {
- // Capture tc.
- tc := testCases[tci]
- tests = append(tests, testing.InternalTest{
- Name: fmt.Sprintf("%s_%s", tc.Suite, tc.Name),
- F: func(t *testing.T) {
- if *platform == "native" {
- // Run the test case on host.
- runTestCaseNative(testBin, tc, t)
- } else {
- // Run the test case in runsc.
- runTestCaseRunsc(testBin, tc, t)
- }
- },
- })
- }
-
- testing.Main(matchString, tests, nil, nil)
-}
diff --git a/test/runtimes/proctor/BUILD b/test/runtimes/proctor/BUILD
index fdc6d3173..b4a9b12de 100644
--- a/test/runtimes/proctor/BUILD
+++ b/test/runtimes/proctor/BUILD
@@ -7,5 +7,8 @@ go_binary(
srcs = ["main.go"],
pure = True,
visibility = ["//test/runtimes:__pkg__"],
- deps = ["//test/runtimes/proctor/lib"],
+ deps = [
+ "//test/runtimes/proctor/lib",
+ "@org_golang_x_sys//unix:go_default_library",
+ ],
)
diff --git a/test/runtimes/proctor/lib/BUILD b/test/runtimes/proctor/lib/BUILD
index 0c8367dfe..f834f1b5a 100644
--- a/test/runtimes/proctor/lib/BUILD
+++ b/test/runtimes/proctor/lib/BUILD
@@ -13,6 +13,7 @@ go_library(
"python.go",
],
visibility = ["//test/runtimes/proctor:__pkg__"],
+ deps = ["@org_golang_x_sys//unix:go_default_library"],
)
go_test(
diff --git a/test/runtimes/proctor/lib/lib.go b/test/runtimes/proctor/lib/lib.go
index f2ba82498..36c60088a 100644
--- a/test/runtimes/proctor/lib/lib.go
+++ b/test/runtimes/proctor/lib/lib.go
@@ -22,7 +22,8 @@ import (
"os/signal"
"path/filepath"
"regexp"
- "syscall"
+
+ "golang.org/x/sys/unix"
)
// TestRunner is an interface that must be implemented for each runtime
@@ -59,7 +60,7 @@ func TestRunnerForRuntime(runtime string) (TestRunner, error) {
func PauseAndReap() {
// Get notified of any new children.
ch := make(chan os.Signal, 1)
- signal.Notify(ch, syscall.SIGCHLD)
+ signal.Notify(ch, unix.SIGCHLD)
for {
if _, ok := <-ch; !ok {
@@ -69,7 +70,7 @@ func PauseAndReap() {
// Reap the child.
for {
- if cpid, _ := syscall.Wait4(-1, nil, syscall.WNOHANG, nil); cpid < 1 {
+ if cpid, _ := unix.Wait4(-1, nil, unix.WNOHANG, nil); cpid < 1 {
break
}
}
diff --git a/test/runtimes/proctor/main.go b/test/runtimes/proctor/main.go
index 81cb68381..8c076a499 100644
--- a/test/runtimes/proctor/main.go
+++ b/test/runtimes/proctor/main.go
@@ -22,8 +22,8 @@ import (
"log"
"os"
"strings"
- "syscall"
+ "golang.org/x/sys/unix"
"gvisor.dev/gvisor/test/runtimes/proctor/lib"
)
@@ -42,14 +42,14 @@ func setNumFilesLimit() error {
// timeout if the NOFILE limit is too high. On gVisor, syscalls are
// slower so these tests will need even more time to pass.
const nofile = 32768
- rLimit := syscall.Rlimit{}
- err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
+ rLimit := unix.Rlimit{}
+ err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rLimit)
if err != nil {
return fmt.Errorf("failed to get RLIMIT_NOFILE: %v", err)
}
if rLimit.Cur > nofile {
rLimit.Cur = nofile
- err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
+ err := unix.Setrlimit(unix.RLIMIT_NOFILE, &rLimit)
if err != nil {
return fmt.Errorf("failed to set RLIMIT_NOFILE: %v", err)
}
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD
index 9adb1cea3..ef299799e 100644
--- a/test/syscalls/BUILD
+++ b/test/syscalls/BUILD
@@ -65,14 +65,8 @@ syscall_test(
syscall_test(
size = "large",
- # Produce too many logs in the debug mode.
- debug = False,
shard_count = most_shards,
- # Takes too long for TSAN. Since this is kind of a stress test that doesn't
- # involve much concurrency, TSAN's usefulness here is limited anyway.
- tags = ["nogotsan"],
test = "//test/syscalls/linux:socket_stress_test",
- vfs2 = False,
)
syscall_test(
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index 5371f825c..5399d8106 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -2330,13 +2330,15 @@ cc_binary(
],
linkstatic = 1,
deps = [
+ gtest,
":ip_socket_test_util",
":socket_test_util",
- "@com_google_absl//absl/strings",
- gtest,
+ "//test/util:file_descriptor",
"//test/util:test_main",
"//test/util:test_util",
"//test/util:thread_util",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/time",
],
)
diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc
index e508ce27f..61a421788 100644
--- a/test/syscalls/linux/proc.cc
+++ b/test/syscalls/linux/proc.cc
@@ -2162,7 +2162,13 @@ class BlockingChild {
return tid_;
}
- void Join() { Stop(); }
+ void Join() {
+ {
+ absl::MutexLock ml(&mu_);
+ stop_ = true;
+ }
+ thread_.Join();
+ }
private:
void Start() {
@@ -2172,11 +2178,6 @@ class BlockingChild {
mu_.Await(absl::Condition(&stop_));
}
- void Stop() {
- absl::MutexLock ml(&mu_);
- stop_ = true;
- }
-
mutable absl::Mutex mu_;
bool stop_ ABSL_GUARDED_BY(mu_) = false;
pid_t tid_;
@@ -2190,16 +2191,18 @@ class BlockingChild {
TEST(ProcTask, NewThreadAppears) {
auto initial = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/proc/self/task", false));
BlockingChild child1;
- EXPECT_NO_ERRNO(DirContainsExactly("/proc/self/task",
- TaskFiles(initial, {child1.Tid()})));
+ // Use Eventually* in case a proc from ealier test is still tearing down.
+ EXPECT_NO_ERRNO(EventuallyDirContainsExactly(
+ "/proc/self/task", TaskFiles(initial, {child1.Tid()})));
}
TEST(ProcTask, KilledThreadsDisappear) {
auto initial = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/proc/self/task/", false));
BlockingChild child1;
- EXPECT_NO_ERRNO(DirContainsExactly("/proc/self/task",
- TaskFiles(initial, {child1.Tid()})));
+ // Use Eventually* in case a proc from ealier test is still tearing down.
+ EXPECT_NO_ERRNO(EventuallyDirContainsExactly(
+ "/proc/self/task", TaskFiles(initial, {child1.Tid()})));
// Stat child1's task file. Regression test for b/32097707.
struct stat statbuf;
diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc
index 73140b2e9..20f1dc305 100644
--- a/test/syscalls/linux/proc_net.cc
+++ b/test/syscalls/linux/proc_net.cc
@@ -40,6 +40,7 @@ namespace {
constexpr const char kProcNet[] = "/proc/net";
constexpr const char kIpForward[] = "/proc/sys/net/ipv4/ip_forward";
+constexpr const char kRangeFile[] = "/proc/sys/net/ipv4/ip_local_port_range";
TEST(ProcNetSymlinkTarget, FileMode) {
struct stat s;
@@ -562,6 +563,42 @@ TEST(ProcSysNetIpv4IpForward, CanReadAndWrite) {
EXPECT_EQ(buf, to_write);
}
+TEST(ProcSysNetPortRange, CanReadAndWrite) {
+ int min;
+ int max;
+ std::string rangefile = ASSERT_NO_ERRNO_AND_VALUE(GetContents(kRangeFile));
+ ASSERT_EQ(rangefile.back(), '\n');
+ rangefile.pop_back();
+ std::vector<std::string> range =
+ absl::StrSplit(rangefile, absl::ByAnyChar("\t "));
+ ASSERT_GT(range.size(), 1);
+ ASSERT_TRUE(absl::SimpleAtoi(range.front(), &min));
+ ASSERT_TRUE(absl::SimpleAtoi(range.back(), &max));
+ EXPECT_LE(min, max);
+
+ // If the file isn't writable, there's nothing else to do here.
+ if (access(kRangeFile, W_OK)) {
+ return;
+ }
+
+ constexpr int kSize = 77;
+ FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(kRangeFile, O_WRONLY | O_TRUNC, 0));
+ max = min + kSize;
+ const std::string small_range = absl::StrFormat("%d %d", min, max);
+ ASSERT_THAT(write(fd.get(), small_range.c_str(), small_range.size()),
+ SyscallSucceedsWithValue(small_range.size()));
+
+ rangefile = ASSERT_NO_ERRNO_AND_VALUE(GetContents(kRangeFile));
+ ASSERT_EQ(rangefile.back(), '\n');
+ rangefile.pop_back();
+ range = absl::StrSplit(rangefile, absl::ByAnyChar("\t "));
+ ASSERT_GT(range.size(), 1);
+ ASSERT_TRUE(absl::SimpleAtoi(range.front(), &min));
+ ASSERT_TRUE(absl::SimpleAtoi(range.back(), &max));
+ EXPECT_EQ(min + kSize, max);
+}
+
} // namespace
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc
index 294b9f6fd..8d15c491e 100644
--- a/test/syscalls/linux/pty.cc
+++ b/test/syscalls/linux/pty.cc
@@ -1255,8 +1255,11 @@ TEST_F(PtyTest, PartialBadBuffer) {
// Read from the replica into bad_buffer.
ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), size));
- EXPECT_THAT(ReadFd(replica_.get(), bad_buffer, size),
- SyscallFailsWithErrno(EFAULT));
+ // Before Linux 3b830a9c this returned EFAULT, but after that commit it
+ // returns EAGAIN.
+ EXPECT_THAT(
+ ReadFd(replica_.get(), bad_buffer, size),
+ AnyOf(SyscallFailsWithErrno(EFAULT), SyscallFailsWithErrno(EAGAIN)));
EXPECT_THAT(munmap(addr, 2 * kPageSize), SyscallSucceeds()) << addr;
}
diff --git a/test/syscalls/linux/socket_generic_stress.cc b/test/syscalls/linux/socket_generic_stress.cc
index 679586530..c35aa2183 100644
--- a/test/syscalls/linux/socket_generic_stress.cc
+++ b/test/syscalls/linux/socket_generic_stress.cc
@@ -17,29 +17,72 @@
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <unistd.h>
#include <array>
#include <string>
#include "gtest/gtest.h"
+#include "absl/strings/numbers.h"
+#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/file_descriptor.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
namespace gvisor {
namespace testing {
+constexpr char kRangeFile[] = "/proc/sys/net/ipv4/ip_local_port_range";
+
+PosixErrorOr<int> NumPorts() {
+ int min = 0;
+ int max = 1 << 16;
+
+ // Read the ephemeral range from /proc.
+ ASSIGN_OR_RETURN_ERRNO(std::string rangefile, GetContents(kRangeFile));
+ const std::string err_msg =
+ absl::StrFormat("%s has invalid content: %s", kRangeFile, rangefile);
+ if (rangefile.back() != '\n') {
+ return PosixError(EINVAL, err_msg);
+ }
+ rangefile.pop_back();
+ std::vector<std::string> range =
+ absl::StrSplit(rangefile, absl::ByAnyChar("\t "));
+ if (range.size() < 2 || !absl::SimpleAtoi(range.front(), &min) ||
+ !absl::SimpleAtoi(range.back(), &max)) {
+ return PosixError(EINVAL, err_msg);
+ }
+
+ // If we can open as writable, limit the range.
+ if (!access(kRangeFile, W_OK)) {
+ ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd,
+ Open(kRangeFile, O_WRONLY | O_TRUNC, 0));
+ max = min + 50;
+ const std::string small_range = absl::StrFormat("%d %d", min, max);
+ int n = write(fd.get(), small_range.c_str(), small_range.size());
+ if (n < 0) {
+ return PosixError(
+ errno,
+ absl::StrFormat("write(%d [%s], \"%s\", %d)", fd.get(), kRangeFile,
+ small_range.c_str(), small_range.size()));
+ }
+ }
+ return max - min;
+}
+
// Test fixture for tests that apply to pairs of connected sockets.
using ConnectStressTest = SocketPairTest;
-TEST_P(ConnectStressTest, Reset65kTimes) {
- // TODO(b/165912341): These are too slow on KVM platform with nested virt.
- SKIP_IF(GvisorPlatform() == Platform::kKVM);
-
- for (int i = 0; i < 1 << 16; ++i) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+TEST_P(ConnectStressTest, Reset) {
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ for (int i = 0; i < nports * 2; i++) {
+ const std::unique_ptr<SocketPair> sockets =
+ ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Send some data to ensure that the connection gets reset and the port gets
// released immediately. This avoids either end entering TIME-WAIT.
@@ -57,6 +100,24 @@ TEST_P(ConnectStressTest, Reset65kTimes) {
}
}
+// Tests that opening too many connections -- without closing them -- does lead
+// to port exhaustion.
+TEST_P(ConnectStressTest, TooManyOpen) {
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ int err_num = 0;
+ std::vector<std::unique_ptr<SocketPair>> sockets =
+ std::vector<std::unique_ptr<SocketPair>>(nports);
+ for (int i = 0; i < nports * 2; i++) {
+ PosixErrorOr<std::unique_ptr<SocketPair>> socks = NewSocketPair();
+ if (!socks.ok()) {
+ err_num = socks.error().errno_value();
+ break;
+ }
+ sockets.push_back(std::move(socks).ValueOrDie());
+ }
+ ASSERT_EQ(err_num, EADDRINUSE);
+}
+
INSTANTIATE_TEST_SUITE_P(
AllConnectedSockets, ConnectStressTest,
::testing::Values(IPv6UDPBidirectionalBindSocketPair(0),
@@ -73,14 +134,40 @@ INSTANTIATE_TEST_SUITE_P(
// Test fixture for tests that apply to pairs of connected sockets created with
// a persistent listener (if applicable).
-using PersistentListenerConnectStressTest = SocketPairTest;
+class PersistentListenerConnectStressTest : public SocketPairTest {
+ protected:
+ PersistentListenerConnectStressTest() : slept_{false} {}
-TEST_P(PersistentListenerConnectStressTest, 65kTimesShutdownCloseFirst) {
- // TODO(b/165912341): These are too slow on KVM platform with nested virt.
- SKIP_IF(GvisorPlatform() == Platform::kKVM);
+ // NewSocketSleep is the same as NewSocketPair, but will sleep once (over the
+ // lifetime of the fixture) and retry if creation fails due to EADDRNOTAVAIL.
+ PosixErrorOr<std::unique_ptr<SocketPair>> NewSocketSleep() {
+ // We can't reuse a connection too close in time to its last use, as TCP
+ // uses the timestamp difference to disambiguate connections. With a
+ // sufficiently small port range, we'll cycle through too quickly, and TCP
+ // won't allow for connection reuse. Thus, we sleep the first time
+ // encountering EADDRINUSE to allow for that difference (1 second in
+ // gVisor).
+ PosixErrorOr<std::unique_ptr<SocketPair>> socks = NewSocketPair();
+ if (socks.ok()) {
+ return socks;
+ }
+ if (!slept_ && socks.error().errno_value() == EADDRNOTAVAIL) {
+ absl::SleepFor(absl::Milliseconds(1500));
+ slept_ = true;
+ return NewSocketPair();
+ }
+ return socks;
+ }
- for (int i = 0; i < 1 << 16; ++i) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ private:
+ bool slept_;
+};
+
+TEST_P(PersistentListenerConnectStressTest, ShutdownCloseFirst) {
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ for (int i = 0; i < nports * 2; i++) {
+ std::unique_ptr<SocketPair> sockets =
+ ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
if (GetParam().type == SOCK_STREAM) {
// Poll the other FD to make sure that we see the FIN from the other
@@ -97,12 +184,11 @@ TEST_P(PersistentListenerConnectStressTest, 65kTimesShutdownCloseFirst) {
}
}
-TEST_P(PersistentListenerConnectStressTest, 65kTimesShutdownCloseSecond) {
- // TODO(b/165912341): These are too slow on KVM platform with nested virt.
- SKIP_IF(GvisorPlatform() == Platform::kKVM);
-
- for (int i = 0; i < 1 << 16; ++i) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+TEST_P(PersistentListenerConnectStressTest, ShutdownCloseSecond) {
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ for (int i = 0; i < nports * 2; i++) {
+ const std::unique_ptr<SocketPair> sockets =
+ ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
if (GetParam().type == SOCK_STREAM) {
// Poll the other FD to make sure that we see the FIN from the other
@@ -119,12 +205,11 @@ TEST_P(PersistentListenerConnectStressTest, 65kTimesShutdownCloseSecond) {
}
}
-TEST_P(PersistentListenerConnectStressTest, 65kTimesClose) {
- // TODO(b/165912341): These are too slow on KVM platform with nested virt.
- SKIP_IF(GvisorPlatform() == Platform::kKVM);
-
- for (int i = 0; i < 1 << 16; ++i) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+TEST_P(PersistentListenerConnectStressTest, Close) {
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ for (int i = 0; i < nports * 2; i++) {
+ std::unique_ptr<SocketPair> sockets =
+ ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
}
}
@@ -149,7 +234,8 @@ TEST_P(DataTransferStressTest, BigDataTransfer) {
// TODO(b/165912341): These are too slow on KVM platform with nested virt.
SKIP_IF(GvisorPlatform() == Platform::kKVM);
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ const std::unique_ptr<SocketPair> sockets =
+ ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int client_fd = sockets->first_fd();
int server_fd = sockets->second_fd();
diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc
index 344a5a22c..54b45b075 100644
--- a/test/syscalls/linux/socket_inet_loopback.cc
+++ b/test/syscalls/linux/socket_inet_loopback.cc
@@ -705,12 +705,6 @@ TEST_P(SocketInetLoopbackTest, TCPFinWait2Test_NoRandomSave) {
ds.reset();
- if (!IsRunningOnGvisor()) {
- ASSERT_THAT(
- bind(conn_fd2.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr),
- conn_addrlen),
- SyscallSucceeds());
- }
ASSERT_THAT(RetryEINTR(connect)(conn_fd2.get(),
reinterpret_cast<sockaddr*>(&conn_addr),
conn_addrlen),
diff --git a/test/uds/BUILD b/test/uds/BUILD
index 51e2c7ce8..a8f49b50c 100644
--- a/test/uds/BUILD
+++ b/test/uds/BUILD
@@ -12,5 +12,6 @@ go_library(
deps = [
"//pkg/log",
"//pkg/unet",
+ "@org_golang_x_sys//unix:go_default_library",
],
)
diff --git a/test/uds/uds.go b/test/uds/uds.go
index b714c61b0..02a4a7dee 100644
--- a/test/uds/uds.go
+++ b/test/uds/uds.go
@@ -21,8 +21,8 @@ import (
"io/ioutil"
"os"
"path/filepath"
- "syscall"
+ "golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/unet"
)
@@ -31,16 +31,16 @@ import (
//
// Only works for stream, seqpacket sockets.
func createEchoSocket(path string, protocol int) (cleanup func(), err error) {
- fd, err := syscall.Socket(syscall.AF_UNIX, protocol, 0)
+ fd, err := unix.Socket(unix.AF_UNIX, protocol, 0)
if err != nil {
return nil, fmt.Errorf("error creating echo(%d) socket: %v", protocol, err)
}
- if err := syscall.Bind(fd, &syscall.SockaddrUnix{Name: path}); err != nil {
+ if err := unix.Bind(fd, &unix.SockaddrUnix{Name: path}); err != nil {
return nil, fmt.Errorf("error binding echo(%d) socket: %v", protocol, err)
}
- if err := syscall.Listen(fd, 0); err != nil {
+ if err := unix.Listen(fd, 0); err != nil {
return nil, fmt.Errorf("error listening echo(%d) socket: %v", protocol, err)
}
@@ -97,17 +97,17 @@ func createEchoSocket(path string, protocol int) (cleanup func(), err error) {
//
// Only relevant for stream, seqpacket sockets.
func createNonListeningSocket(path string, protocol int) (cleanup func(), err error) {
- fd, err := syscall.Socket(syscall.AF_UNIX, protocol, 0)
+ fd, err := unix.Socket(unix.AF_UNIX, protocol, 0)
if err != nil {
return nil, fmt.Errorf("error creating nonlistening(%d) socket: %v", protocol, err)
}
- if err := syscall.Bind(fd, &syscall.SockaddrUnix{Name: path}); err != nil {
+ if err := unix.Bind(fd, &unix.SockaddrUnix{Name: path}); err != nil {
return nil, fmt.Errorf("error binding nonlistening(%d) socket: %v", protocol, err)
}
cleanup = func() {
- if err := syscall.Close(fd); err != nil {
+ if err := unix.Close(fd); err != nil {
log.Warningf("Failed to close nonlistening(%d) socket: %v", protocol, err)
}
}
@@ -119,12 +119,12 @@ func createNonListeningSocket(path string, protocol int) (cleanup func(), err er
//
// Only works for dgram sockets.
func createNullSocket(path string, protocol int) (cleanup func(), err error) {
- fd, err := syscall.Socket(syscall.AF_UNIX, protocol, 0)
+ fd, err := unix.Socket(unix.AF_UNIX, protocol, 0)
if err != nil {
return nil, fmt.Errorf("error creating null(%d) socket: %v", protocol, err)
}
- if err := syscall.Bind(fd, &syscall.SockaddrUnix{Name: path}); err != nil {
+ if err := unix.Bind(fd, &unix.SockaddrUnix{Name: path}); err != nil {
return nil, fmt.Errorf("error binding null(%d) socket: %v", protocol, err)
}
@@ -174,7 +174,7 @@ func CreateSocketTree(baseDir string) (dir string, cleanup func(), err error) {
sockets map[string]socketCreator
}{
{
- protocol: syscall.SOCK_STREAM,
+ protocol: unix.SOCK_STREAM,
name: "stream",
sockets: map[string]socketCreator{
"echo": createEchoSocket,
@@ -182,7 +182,7 @@ func CreateSocketTree(baseDir string) (dir string, cleanup func(), err error) {
},
},
{
- protocol: syscall.SOCK_SEQPACKET,
+ protocol: unix.SOCK_SEQPACKET,
name: "seqpacket",
sockets: map[string]socketCreator{
"echo": createEchoSocket,
@@ -190,7 +190,7 @@ func CreateSocketTree(baseDir string) (dir string, cleanup func(), err error) {
},
},
{
- protocol: syscall.SOCK_DGRAM,
+ protocol: unix.SOCK_DGRAM,
name: "dgram",
sockets: map[string]socketCreator{
"null": createNullSocket,