summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/e2e/integration_test.go15
-rw-r--r--test/iptables/filter_output.go169
-rw-r--r--test/iptables/iptables_test.go20
-rw-r--r--test/packetimpact/testbench/BUILD1
-rw-r--r--test/packetimpact/testbench/connections.go24
-rw-r--r--test/packetimpact/testbench/dut.go52
-rw-r--r--test/packetimpact/testbench/dut_client.go6
-rw-r--r--test/packetimpact/testbench/rawsockets.go4
-rw-r--r--test/packetimpact/testbench/testbench.go63
-rw-r--r--test/packetimpact/tests/BUILD10
-rw-r--r--test/packetimpact/tests/fin_wait2_timeout_test.go5
-rw-r--r--test/packetimpact/tests/icmpv6_param_problem_test.go5
-rw-r--r--test/packetimpact/tests/tcp_close_wait_ack_test.go5
-rw-r--r--test/packetimpact/tests/tcp_noaccept_close_rst_test.go5
-rw-r--r--test/packetimpact/tests/tcp_outside_the_window_test.go5
-rw-r--r--test/packetimpact/tests/tcp_retransmits_test.go84
-rw-r--r--test/packetimpact/tests/tcp_should_piggyback_test.go5
-rw-r--r--test/packetimpact/tests/tcp_user_timeout_test.go5
-rw-r--r--test/packetimpact/tests/tcp_window_shrink_test.go5
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go5
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_test.go5
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go5
-rw-r--r--test/packetimpact/tests/udp_icmp_error_propagation_test.go43
-rw-r--r--test/packetimpact/tests/udp_recv_multicast_test.go5
-rw-r--r--test/packetimpact/tests/udp_send_recv_dgram_test.go5
-rw-r--r--test/syscalls/linux/BUILD2
-rw-r--r--test/syscalls/linux/fpsig_fork.cc17
-rw-r--r--test/syscalls/linux/socket.cc9
-rw-r--r--test/syscalls/linux/socket_bind_to_device_sequence.cc5
-rw-r--r--test/syscalls/linux/socket_ip_tcp_generic.cc45
-rw-r--r--test/syscalls/linux/symlink.cc25
-rw-r--r--test/syscalls/linux/tcp_socket.cc185
32 files changed, 747 insertions, 97 deletions
diff --git a/test/e2e/integration_test.go b/test/e2e/integration_test.go
index 404e37689..ff856883a 100644
--- a/test/e2e/integration_test.go
+++ b/test/e2e/integration_test.go
@@ -361,6 +361,21 @@ func TestTmpFile(t *testing.T) {
}
}
+// TestHostOverlayfsCopyUp tests that the --overlayfs-stale-read option causes
+// runsc to hide the incoherence of FDs opened before and after overlayfs
+// copy-up on the host.
+func TestHostOverlayfsCopyUp(t *testing.T) {
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
+
+ if _, err := d.Run(dockerutil.RunOpts{
+ Image: "hostoverlaytest",
+ WorkDir: "/root",
+ }, "./test"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+}
+
func TestMain(m *testing.M) {
dockerutil.EnsureSupportedDockerVersion()
flag.Parse()
diff --git a/test/iptables/filter_output.go b/test/iptables/filter_output.go
index c145bd1e9..ba0d6fc29 100644
--- a/test/iptables/filter_output.go
+++ b/test/iptables/filter_output.go
@@ -29,6 +29,11 @@ func init() {
RegisterTestCase(FilterOutputAcceptUDPOwner{})
RegisterTestCase(FilterOutputDropUDPOwner{})
RegisterTestCase(FilterOutputOwnerFail{})
+ RegisterTestCase(FilterOutputAcceptGIDOwner{})
+ RegisterTestCase(FilterOutputDropGIDOwner{})
+ RegisterTestCase(FilterOutputInvertGIDOwner{})
+ RegisterTestCase(FilterOutputInvertUIDOwner{})
+ RegisterTestCase(FilterOutputInvertUIDAndGIDOwner{})
RegisterTestCase(FilterOutputInterfaceAccept{})
RegisterTestCase(FilterOutputInterfaceDrop{})
RegisterTestCase(FilterOutputInterface{})
@@ -116,20 +121,12 @@ func (FilterOutputAcceptTCPOwner) ContainerAction(ip net.IP) error {
}
// Listen for TCP packets on accept port.
- if err := listenTCP(acceptPort, sendloopDuration); err != nil {
- return fmt.Errorf("connection on port %d should be accepted, but got dropped", acceptPort)
- }
-
- return nil
+ return listenTCP(acceptPort, sendloopDuration)
}
// LocalAction implements TestCase.LocalAction.
func (FilterOutputAcceptTCPOwner) LocalAction(ip net.IP) error {
- if err := connectTCP(ip, acceptPort, sendloopDuration); err != nil {
- return fmt.Errorf("connection destined to port %d should be accepted, but got dropped", acceptPort)
- }
-
- return nil
+ return connectTCP(ip, acceptPort, sendloopDuration)
}
// FilterOutputDropTCPOwner tests that TCP connections from uid owner are dropped.
@@ -239,6 +236,158 @@ func (FilterOutputOwnerFail) LocalAction(ip net.IP) error {
return nil
}
+// FilterOutputAcceptGIDOwner tests that TCP connections from gid owner are accepted.
+type FilterOutputAcceptGIDOwner struct{}
+
+// Name implements TestCase.Name.
+func (FilterOutputAcceptGIDOwner) Name() string {
+ return "FilterOutputAcceptGIDOwner"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterOutputAcceptGIDOwner) ContainerAction(ip net.IP) error {
+ if err := filterTable("-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--gid-owner", "root", "-j", "ACCEPT"); err != nil {
+ return err
+ }
+
+ // Listen for TCP packets on accept port.
+ return listenTCP(acceptPort, sendloopDuration)
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterOutputAcceptGIDOwner) LocalAction(ip net.IP) error {
+ return connectTCP(ip, acceptPort, sendloopDuration)
+}
+
+// FilterOutputDropGIDOwner tests that TCP connections from gid owner are dropped.
+type FilterOutputDropGIDOwner struct{}
+
+// Name implements TestCase.Name.
+func (FilterOutputDropGIDOwner) Name() string {
+ return "FilterOutputDropGIDOwner"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterOutputDropGIDOwner) ContainerAction(ip net.IP) error {
+ if err := filterTable("-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--gid-owner", "root", "-j", "DROP"); err != nil {
+ return err
+ }
+
+ // Listen for TCP packets on accept port.
+ if err := listenTCP(acceptPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection on port %d should not be accepted, but got accepted", acceptPort)
+ }
+
+ return nil
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterOutputDropGIDOwner) LocalAction(ip net.IP) error {
+ if err := connectTCP(ip, acceptPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort)
+ }
+
+ return nil
+}
+
+// FilterOutputInvertGIDOwner tests that TCP connections from gid owner are dropped.
+type FilterOutputInvertGIDOwner struct{}
+
+// Name implements TestCase.Name.
+func (FilterOutputInvertGIDOwner) Name() string {
+ return "FilterOutputInvertGIDOwner"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterOutputInvertGIDOwner) ContainerAction(ip net.IP) error {
+ rules := [][]string{
+ {"-A", "OUTPUT", "-p", "tcp", "-m", "owner", "!", "--gid-owner", "root", "-j", "ACCEPT"},
+ {"-A", "OUTPUT", "-p", "tcp", "-j", "DROP"},
+ }
+ if err := filterTableRules(rules); err != nil {
+ return err
+ }
+
+ // Listen for TCP packets on accept port.
+ if err := listenTCP(acceptPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection on port %d should not be accepted, but got accepted", acceptPort)
+ }
+
+ return nil
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterOutputInvertGIDOwner) LocalAction(ip net.IP) error {
+ if err := connectTCP(ip, acceptPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort)
+ }
+
+ return nil
+}
+
+// FilterOutputInvertUIDOwner tests that TCP connections from gid owner are dropped.
+type FilterOutputInvertUIDOwner struct{}
+
+// Name implements TestCase.Name.
+func (FilterOutputInvertUIDOwner) Name() string {
+ return "FilterOutputInvertUIDOwner"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterOutputInvertUIDOwner) ContainerAction(ip net.IP) error {
+ rules := [][]string{
+ {"-A", "OUTPUT", "-p", "tcp", "-m", "owner", "!", "--uid-owner", "root", "-j", "DROP"},
+ {"-A", "OUTPUT", "-p", "tcp", "-j", "ACCEPT"},
+ }
+ if err := filterTableRules(rules); err != nil {
+ return err
+ }
+
+ // Listen for TCP packets on accept port.
+ return listenTCP(acceptPort, sendloopDuration)
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterOutputInvertUIDOwner) LocalAction(ip net.IP) error {
+ return connectTCP(ip, acceptPort, sendloopDuration)
+}
+
+// FilterOutputInvertUIDAndGIDOwner tests that TCP connections from uid and gid
+// owner are dropped.
+type FilterOutputInvertUIDAndGIDOwner struct{}
+
+// Name implements TestCase.Name.
+func (FilterOutputInvertUIDAndGIDOwner) Name() string {
+ return "FilterOutputInvertUIDAndGIDOwner"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterOutputInvertUIDAndGIDOwner) ContainerAction(ip net.IP) error {
+ rules := [][]string{
+ {"-A", "OUTPUT", "-p", "tcp", "-m", "owner", "!", "--uid-owner", "root", "!", "--gid-owner", "root", "-j", "ACCEPT"},
+ {"-A", "OUTPUT", "-p", "tcp", "-j", "DROP"},
+ }
+ if err := filterTableRules(rules); err != nil {
+ return err
+ }
+
+ // Listen for TCP packets on accept port.
+ if err := listenTCP(acceptPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection on port %d should not be accepted, but got accepted", acceptPort)
+ }
+
+ return nil
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterOutputInvertUIDAndGIDOwner) LocalAction(ip net.IP) error {
+ if err := connectTCP(ip, acceptPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort)
+ }
+
+ return nil
+}
+
// FilterOutputDestination tests that we can selectively allow packets to
// certain destinations.
type FilterOutputDestination struct{}
diff --git a/test/iptables/iptables_test.go b/test/iptables/iptables_test.go
index 84eb75a40..4fd2cb46a 100644
--- a/test/iptables/iptables_test.go
+++ b/test/iptables/iptables_test.go
@@ -167,6 +167,26 @@ func TestFilterOutputOwnerFail(t *testing.T) {
singleTest(t, FilterOutputOwnerFail{})
}
+func TestFilterOutputAcceptGIDOwner(t *testing.T) {
+ singleTest(t, FilterOutputAcceptGIDOwner{})
+}
+
+func TestFilterOutputDropGIDOwner(t *testing.T) {
+ singleTest(t, FilterOutputDropGIDOwner{})
+}
+
+func TestFilterOutputInvertGIDOwner(t *testing.T) {
+ singleTest(t, FilterOutputInvertGIDOwner{})
+}
+
+func TestFilterOutputInvertUIDOwner(t *testing.T) {
+ singleTest(t, FilterOutputInvertUIDOwner{})
+}
+
+func TestFilterOutputInvertUIDAndGIDOwner(t *testing.T) {
+ singleTest(t, FilterOutputInvertUIDAndGIDOwner{})
+}
+
func TestFilterOutputInterfaceAccept(t *testing.T) {
singleTest(t, FilterOutputInterfaceAccept{})
}
diff --git a/test/packetimpact/testbench/BUILD b/test/packetimpact/testbench/BUILD
index 3ceceb9d7..fed51006f 100644
--- a/test/packetimpact/testbench/BUILD
+++ b/test/packetimpact/testbench/BUILD
@@ -13,6 +13,7 @@ go_library(
"dut_client.go",
"layers.go",
"rawsockets.go",
+ "testbench.go",
],
deps = [
"//pkg/tcpip",
diff --git a/test/packetimpact/testbench/connections.go b/test/packetimpact/testbench/connections.go
index 28c841612..463fd0556 100644
--- a/test/packetimpact/testbench/connections.go
+++ b/test/packetimpact/testbench/connections.go
@@ -17,7 +17,6 @@
package testbench
import (
- "flag"
"fmt"
"math/rand"
"net"
@@ -32,13 +31,6 @@ import (
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
)
-var localIPv4 = flag.String("local_ipv4", "", "local IPv4 address for test packets")
-var remoteIPv4 = flag.String("remote_ipv4", "", "remote IPv4 address for test packets")
-var localIPv6 = flag.String("local_ipv6", "", "local IPv6 address for test packets")
-var remoteIPv6 = flag.String("remote_ipv6", "", "remote IPv6 address for test packets")
-var localMAC = flag.String("local_mac", "", "local mac address for test packets")
-var remoteMAC = flag.String("remote_mac", "", "remote mac address for test packets")
-
func portFromSockaddr(sa unix.Sockaddr) (uint16, error) {
switch sa := sa.(type) {
case *unix.SockaddrInet4:
@@ -64,11 +56,11 @@ func pickPort(domain, typ int) (fd int, sa unix.Sockaddr, err error) {
switch domain {
case unix.AF_INET:
var sa4 unix.SockaddrInet4
- copy(sa4.Addr[:], net.ParseIP(*localIPv4).To4())
+ copy(sa4.Addr[:], net.ParseIP(LocalIPv4).To4())
sa = &sa4
case unix.AF_INET6:
var sa6 unix.SockaddrInet6
- copy(sa6.Addr[:], net.ParseIP(*localIPv6).To16())
+ copy(sa6.Addr[:], net.ParseIP(LocalIPv6).To16())
sa = &sa6
default:
return -1, nil, fmt.Errorf("invalid domain %d, it should be one of unix.AF_INET or unix.AF_INET6", domain)
@@ -120,12 +112,12 @@ var _ layerState = (*etherState)(nil)
// newEtherState creates a new etherState.
func newEtherState(out, in Ether) (*etherState, error) {
- lMAC, err := tcpip.ParseMACAddress(*localMAC)
+ lMAC, err := tcpip.ParseMACAddress(LocalMAC)
if err != nil {
return nil, err
}
- rMAC, err := tcpip.ParseMACAddress(*remoteMAC)
+ rMAC, err := tcpip.ParseMACAddress(RemoteMAC)
if err != nil {
return nil, err
}
@@ -172,8 +164,8 @@ var _ layerState = (*ipv4State)(nil)
// newIPv4State creates a new ipv4State.
func newIPv4State(out, in IPv4) (*ipv4State, error) {
- lIP := tcpip.Address(net.ParseIP(*localIPv4).To4())
- rIP := tcpip.Address(net.ParseIP(*remoteIPv4).To4())
+ lIP := tcpip.Address(net.ParseIP(LocalIPv4).To4())
+ rIP := tcpip.Address(net.ParseIP(RemoteIPv4).To4())
s := ipv4State{
out: IPv4{SrcAddr: &lIP, DstAddr: &rIP},
in: IPv4{SrcAddr: &rIP, DstAddr: &lIP},
@@ -217,8 +209,8 @@ var _ layerState = (*ipv6State)(nil)
// newIPv6State creates a new ipv6State.
func newIPv6State(out, in IPv6) (*ipv6State, error) {
- lIP := tcpip.Address(net.ParseIP(*localIPv6).To16())
- rIP := tcpip.Address(net.ParseIP(*remoteIPv6).To16())
+ lIP := tcpip.Address(net.ParseIP(LocalIPv6).To16())
+ rIP := tcpip.Address(net.ParseIP(RemoteIPv6).To16())
s := ipv6State{
out: IPv6{SrcAddr: &lIP, DstAddr: &rIP},
in: IPv6{SrcAddr: &rIP, DstAddr: &lIP},
diff --git a/test/packetimpact/testbench/dut.go b/test/packetimpact/testbench/dut.go
index f68d9d62b..a78b7d7ee 100644
--- a/test/packetimpact/testbench/dut.go
+++ b/test/packetimpact/testbench/dut.go
@@ -16,12 +16,10 @@ package testbench
import (
"context"
- "flag"
"net"
"strconv"
"syscall"
"testing"
- "time"
pb "gvisor.dev/gvisor/test/packetimpact/proto/posix_server_go_proto"
@@ -30,29 +28,21 @@ import (
"google.golang.org/grpc/keepalive"
)
-var (
- posixServerIP = flag.String("posix_server_ip", "", "ip address to listen to for UDP commands")
- posixServerPort = flag.Int("posix_server_port", 40000, "port to listen to for UDP commands")
- rpcTimeout = flag.Duration("rpc_timeout", 100*time.Millisecond, "gRPC timeout")
- rpcKeepalive = flag.Duration("rpc_keepalive", 10*time.Second, "gRPC keepalive")
-)
-
// DUT communicates with the DUT to force it to make POSIX calls.
type DUT struct {
t *testing.T
conn *grpc.ClientConn
- posixServer PosixClient
+ posixServer POSIXClient
}
// NewDUT creates a new connection with the DUT over gRPC.
func NewDUT(t *testing.T) DUT {
- flag.Parse()
- posixServerAddress := *posixServerIP + ":" + strconv.Itoa(*posixServerPort)
- conn, err := grpc.Dial(posixServerAddress, grpc.WithInsecure(), grpc.WithKeepaliveParams(keepalive.ClientParameters{Timeout: *rpcKeepalive}))
+ posixServerAddress := POSIXServerIP + ":" + strconv.Itoa(POSIXServerPort)
+ conn, err := grpc.Dial(posixServerAddress, grpc.WithInsecure(), grpc.WithKeepaliveParams(keepalive.ClientParameters{Timeout: RPCKeepalive}))
if err != nil {
t.Fatalf("failed to grpc.Dial(%s): %s", posixServerAddress, err)
}
- posixServer := NewPosixClient(conn)
+ posixServer := NewPOSIXClient(conn)
return DUT{
t: t,
conn: conn,
@@ -149,12 +139,12 @@ func (dut *DUT) CreateBoundSocket(typ, proto int32, addr net.IP) (int32, uint16)
// CreateListener makes a new TCP connection. If it fails, the test ends.
func (dut *DUT) CreateListener(typ, proto, backlog int32) (int32, uint16) {
- fd, remotePort := dut.CreateBoundSocket(typ, proto, net.ParseIP(*remoteIPv4))
+ fd, remotePort := dut.CreateBoundSocket(typ, proto, net.ParseIP(RemoteIPv4))
dut.Listen(fd, backlog)
return fd, remotePort
}
-// All the functions that make gRPC calls to the Posix service are below, sorted
+// All the functions that make gRPC calls to the POSIX service are below, sorted
// alphabetically.
// Accept calls accept on the DUT and causes a fatal test failure if it doesn't
@@ -162,7 +152,7 @@ func (dut *DUT) CreateListener(typ, proto, backlog int32) (int32, uint16) {
// AcceptWithErrno.
func (dut *DUT) Accept(sockfd int32) (int32, unix.Sockaddr) {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
fd, sa, err := dut.AcceptWithErrno(ctx, sockfd)
if fd < 0 {
@@ -189,7 +179,7 @@ func (dut *DUT) AcceptWithErrno(ctx context.Context, sockfd int32) (int32, unix.
// needed, use BindWithErrno.
func (dut *DUT) Bind(fd int32, sa unix.Sockaddr) {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, err := dut.BindWithErrno(ctx, fd, sa)
if ret != 0 {
@@ -216,7 +206,7 @@ func (dut *DUT) BindWithErrno(ctx context.Context, fd int32, sa unix.Sockaddr) (
// CloseWithErrno.
func (dut *DUT) Close(fd int32) {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, err := dut.CloseWithErrno(ctx, fd)
if ret != 0 {
@@ -242,7 +232,7 @@ func (dut *DUT) CloseWithErrno(ctx context.Context, fd int32) (int32, error) {
// needed, use ConnectWithErrno.
func (dut *DUT) Connect(fd int32, sa unix.Sockaddr) {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, err := dut.ConnectWithErrno(ctx, fd, sa)
if ret != 0 {
@@ -269,7 +259,7 @@ func (dut *DUT) ConnectWithErrno(ctx context.Context, fd int32, sa unix.Sockaddr
// needed, use GetSockNameWithErrno.
func (dut *DUT) GetSockName(sockfd int32) unix.Sockaddr {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, sa, err := dut.GetSockNameWithErrno(ctx, sockfd)
if ret != 0 {
@@ -318,7 +308,7 @@ func (dut *DUT) getSockOpt(ctx context.Context, sockfd, level, optname, optlen i
// more specific GetSockOptXxx function.
func (dut *DUT) GetSockOpt(sockfd, level, optname, optlen int32) []byte {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, optval, err := dut.GetSockOptWithErrno(ctx, sockfd, level, optname, optlen)
if ret != 0 {
@@ -345,7 +335,7 @@ func (dut *DUT) GetSockOptWithErrno(ctx context.Context, sockfd, level, optname,
// is needed, use GetSockOptIntWithErrno.
func (dut *DUT) GetSockOptInt(sockfd, level, optname int32) int32 {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, intval, err := dut.GetSockOptIntWithErrno(ctx, sockfd, level, optname)
if ret != 0 {
@@ -370,7 +360,7 @@ func (dut *DUT) GetSockOptIntWithErrno(ctx context.Context, sockfd, level, optna
// needed, use GetSockOptTimevalWithErrno.
func (dut *DUT) GetSockOptTimeval(sockfd, level, optname int32) unix.Timeval {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, timeval, err := dut.GetSockOptTimevalWithErrno(ctx, sockfd, level, optname)
if ret != 0 {
@@ -399,7 +389,7 @@ func (dut *DUT) GetSockOptTimevalWithErrno(ctx context.Context, sockfd, level, o
// ListenWithErrno.
func (dut *DUT) Listen(sockfd, backlog int32) {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, err := dut.ListenWithErrno(ctx, sockfd, backlog)
if ret != 0 {
@@ -426,7 +416,7 @@ func (dut *DUT) ListenWithErrno(ctx context.Context, sockfd, backlog int32) (int
// SendWithErrno.
func (dut *DUT) Send(sockfd int32, buf []byte, flags int32) int32 {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, err := dut.SendWithErrno(ctx, sockfd, buf, flags)
if ret == -1 {
@@ -455,7 +445,7 @@ func (dut *DUT) SendWithErrno(ctx context.Context, sockfd int32, buf []byte, fla
// SendToWithErrno.
func (dut *DUT) SendTo(sockfd int32, buf []byte, flags int32, destAddr unix.Sockaddr) int32 {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, err := dut.SendToWithErrno(ctx, sockfd, buf, flags, destAddr)
if ret == -1 {
@@ -502,7 +492,7 @@ func (dut *DUT) setSockOpt(ctx context.Context, sockfd, level, optname int32, op
// more specific SetSockOptXxx function.
func (dut *DUT) SetSockOpt(sockfd, level, optname int32, optval []byte) {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, err := dut.SetSockOptWithErrno(ctx, sockfd, level, optname, optval)
if ret != 0 {
@@ -523,7 +513,7 @@ func (dut *DUT) SetSockOptWithErrno(ctx context.Context, sockfd, level, optname
// is needed, use SetSockOptIntWithErrno.
func (dut *DUT) SetSockOptInt(sockfd, level, optname, optval int32) {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, err := dut.SetSockOptIntWithErrno(ctx, sockfd, level, optname, optval)
if ret != 0 {
@@ -542,7 +532,7 @@ func (dut *DUT) SetSockOptIntWithErrno(ctx context.Context, sockfd, level, optna
// needed, use SetSockOptTimevalWithErrno.
func (dut *DUT) SetSockOptTimeval(sockfd, level, optname int32, tv *unix.Timeval) {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, err := dut.SetSockOptTimevalWithErrno(ctx, sockfd, level, optname, tv)
if ret != 0 {
@@ -593,7 +583,7 @@ func (dut *DUT) SocketWithErrno(domain, typ, proto int32) (int32, error) {
// RecvWithErrno.
func (dut *DUT) Recv(sockfd, len, flags int32) []byte {
dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
defer cancel()
ret, buf, err := dut.RecvWithErrno(ctx, sockfd, len, flags)
if ret == -1 {
diff --git a/test/packetimpact/testbench/dut_client.go b/test/packetimpact/testbench/dut_client.go
index b130a33a2..d0e68c5da 100644
--- a/test/packetimpact/testbench/dut_client.go
+++ b/test/packetimpact/testbench/dut_client.go
@@ -20,9 +20,9 @@ import (
)
// PosixClient is a gRPC client for the Posix service.
-type PosixClient pb.PosixClient
+type POSIXClient pb.PosixClient
-// NewPosixClient makes a new gRPC client for the Posix service.
-func NewPosixClient(c grpc.ClientConnInterface) PosixClient {
+// NewPOSIXClient makes a new gRPC client for the POSIX service.
+func NewPOSIXClient(c grpc.ClientConnInterface) POSIXClient {
return pb.NewPosixClient(c)
}
diff --git a/test/packetimpact/testbench/rawsockets.go b/test/packetimpact/testbench/rawsockets.go
index a9ad72b63..4665f60b2 100644
--- a/test/packetimpact/testbench/rawsockets.go
+++ b/test/packetimpact/testbench/rawsockets.go
@@ -27,8 +27,6 @@ import (
"gvisor.dev/gvisor/pkg/usermem"
)
-var device = flag.String("device", "", "local device for test packets")
-
// Sniffer can sniff raw packets on the wire.
type Sniffer struct {
t *testing.T
@@ -139,7 +137,7 @@ type Injector struct {
// NewInjector creates a new injector on *device.
func NewInjector(t *testing.T) (Injector, error) {
flag.Parse()
- ifInfo, err := net.InterfaceByName(*device)
+ ifInfo, err := net.InterfaceByName(Device)
if err != nil {
return Injector{}, err
}
diff --git a/test/packetimpact/testbench/testbench.go b/test/packetimpact/testbench/testbench.go
new file mode 100644
index 000000000..a1242b189
--- /dev/null
+++ b/test/packetimpact/testbench/testbench.go
@@ -0,0 +1,63 @@
+// Copyright 2020 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package testbench
+
+import (
+ "flag"
+ "time"
+)
+
+var (
+ // Device is the local device on the test network.
+ Device = ""
+ // LocalIPv4 is the local IPv4 address on the test network.
+ LocalIPv4 = ""
+ // LocalIPv6 is the local IPv6 address on the test network.
+ LocalIPv6 = ""
+ // LocalMAC is the local MAC address on the test network.
+ LocalMAC = ""
+ // POSIXServerIP is the POSIX server's IP address on the control network.
+ POSIXServerIP = ""
+ // POSIXServerPort is the UDP port the POSIX server is bound to on the
+ // control network.
+ POSIXServerPort = 40000
+ // RemoteIPv4 is the DUT's IPv4 address on the test network.
+ RemoteIPv4 = ""
+ // RemoteIPv6 is the DUT's IPv6 address on the test network.
+ RemoteIPv6 = ""
+ // RemoteMAC is the DUT's MAC address on the test network.
+ RemoteMAC = ""
+ // RPCKeepalive is the gRPC keepalive.
+ RPCKeepalive = 10 * time.Second
+ // RPCTimeout is the gRPC timeout.
+ RPCTimeout = 100 * time.Millisecond
+)
+
+// RegisterFlags defines flags and associates them with the package-level
+// exported variables above. It should be called by tests in their init
+// functions.
+func RegisterFlags(fs *flag.FlagSet) {
+ fs.StringVar(&POSIXServerIP, "posix_server_ip", POSIXServerIP, "ip address to listen to for UDP commands")
+ fs.IntVar(&POSIXServerPort, "posix_server_port", POSIXServerPort, "port to listen to for UDP commands")
+ fs.DurationVar(&RPCTimeout, "rpc_timeout", RPCTimeout, "gRPC timeout")
+ fs.DurationVar(&RPCKeepalive, "rpc_keepalive", RPCKeepalive, "gRPC keepalive")
+ fs.StringVar(&LocalIPv4, "local_ipv4", LocalIPv4, "local IPv4 address for test packets")
+ fs.StringVar(&RemoteIPv4, "remote_ipv4", RemoteIPv4, "remote IPv4 address for test packets")
+ fs.StringVar(&LocalIPv6, "local_ipv6", LocalIPv6, "local IPv6 address for test packets")
+ fs.StringVar(&RemoteIPv6, "remote_ipv6", RemoteIPv6, "remote IPv6 address for test packets")
+ fs.StringVar(&LocalMAC, "local_mac", LocalMAC, "local mac address for test packets")
+ fs.StringVar(&RemoteMAC, "remote_mac", RemoteMAC, "remote mac address for test packets")
+ fs.StringVar(&Device, "device", Device, "local device for test packets")
+}
diff --git a/test/packetimpact/tests/BUILD b/test/packetimpact/tests/BUILD
index c25b3b8c1..fff7787c8 100644
--- a/test/packetimpact/tests/BUILD
+++ b/test/packetimpact/tests/BUILD
@@ -82,6 +82,16 @@ packetimpact_go_test(
)
packetimpact_go_test(
+ name = "tcp_retransmits",
+ srcs = ["tcp_retransmits_test.go"],
+ deps = [
+ "//pkg/tcpip/header",
+ "//test/packetimpact/testbench",
+ "@org_golang_x_sys//unix:go_default_library",
+ ],
+)
+
+packetimpact_go_test(
name = "tcp_outside_the_window",
srcs = ["tcp_outside_the_window_test.go"],
deps = [
diff --git a/test/packetimpact/tests/fin_wait2_timeout_test.go b/test/packetimpact/tests/fin_wait2_timeout_test.go
index 99dc77f9a..c26ab78d9 100644
--- a/test/packetimpact/tests/fin_wait2_timeout_test.go
+++ b/test/packetimpact/tests/fin_wait2_timeout_test.go
@@ -15,6 +15,7 @@
package fin_wait2_timeout_test
import (
+ "flag"
"testing"
"time"
@@ -23,6 +24,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
func TestFinWait2Timeout(t *testing.T) {
for _, tt := range []struct {
description string
diff --git a/test/packetimpact/tests/icmpv6_param_problem_test.go b/test/packetimpact/tests/icmpv6_param_problem_test.go
index b48e55df4..bb1fc26fc 100644
--- a/test/packetimpact/tests/icmpv6_param_problem_test.go
+++ b/test/packetimpact/tests/icmpv6_param_problem_test.go
@@ -16,6 +16,7 @@ package icmpv6_param_problem_test
import (
"encoding/binary"
+ "flag"
"testing"
"time"
@@ -23,6 +24,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
// TestICMPv6ParamProblemTest sends a packet with a bad next header. The DUT
// should respond with an ICMPv6 Parameter Problem message.
func TestICMPv6ParamProblemTest(t *testing.T) {
diff --git a/test/packetimpact/tests/tcp_close_wait_ack_test.go b/test/packetimpact/tests/tcp_close_wait_ack_test.go
index 153ce285b..70a22a2db 100644
--- a/test/packetimpact/tests/tcp_close_wait_ack_test.go
+++ b/test/packetimpact/tests/tcp_close_wait_ack_test.go
@@ -15,6 +15,7 @@
package tcp_close_wait_ack_test
import (
+ "flag"
"fmt"
"testing"
"time"
@@ -25,6 +26,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
func TestCloseWaitAck(t *testing.T) {
for _, tt := range []struct {
description string
diff --git a/test/packetimpact/tests/tcp_noaccept_close_rst_test.go b/test/packetimpact/tests/tcp_noaccept_close_rst_test.go
index 7ebdd1950..2c1ec27d3 100644
--- a/test/packetimpact/tests/tcp_noaccept_close_rst_test.go
+++ b/test/packetimpact/tests/tcp_noaccept_close_rst_test.go
@@ -15,6 +15,7 @@
package tcp_noaccept_close_rst_test
import (
+ "flag"
"testing"
"time"
@@ -23,6 +24,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
func TestTcpNoAcceptCloseReset(t *testing.T) {
dut := tb.NewDUT(t)
defer dut.TearDown()
diff --git a/test/packetimpact/tests/tcp_outside_the_window_test.go b/test/packetimpact/tests/tcp_outside_the_window_test.go
index db3d3273b..351df193e 100644
--- a/test/packetimpact/tests/tcp_outside_the_window_test.go
+++ b/test/packetimpact/tests/tcp_outside_the_window_test.go
@@ -15,6 +15,7 @@
package tcp_outside_the_window_test
import (
+ "flag"
"fmt"
"testing"
"time"
@@ -25,6 +26,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
// TestTCPOutsideTheWindows tests the behavior of the DUT when packets arrive
// that are inside or outside the TCP window. Packets that are outside the
// window should force an extra ACK, as described in RFC793 page 69:
diff --git a/test/packetimpact/tests/tcp_retransmits_test.go b/test/packetimpact/tests/tcp_retransmits_test.go
new file mode 100644
index 000000000..c043ad881
--- /dev/null
+++ b/test/packetimpact/tests/tcp_retransmits_test.go
@@ -0,0 +1,84 @@
+// 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_retransmits_test
+
+import (
+ "flag"
+ "testing"
+ "time"
+
+ "golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/tcpip/header"
+ tb "gvisor.dev/gvisor/test/packetimpact/testbench"
+)
+
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
+// TestRetransmits tests retransmits occur at exponentially increasing
+// time intervals.
+func TestRetransmits(t *testing.T) {
+ dut := tb.NewDUT(t)
+ defer dut.TearDown()
+ listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
+ defer dut.Close(listenFd)
+ conn := tb.NewTCPIPv4(t, tb.TCP{DstPort: &remotePort}, tb.TCP{SrcPort: &remotePort})
+ defer conn.Close()
+
+ conn.Handshake()
+ acceptFd, _ := dut.Accept(listenFd)
+ defer dut.Close(acceptFd)
+
+ dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
+
+ sampleData := []byte("Sample Data")
+ samplePayload := &tb.Payload{Bytes: sampleData}
+
+ dut.Send(acceptFd, sampleData, 0)
+ if _, err := conn.ExpectData(&tb.TCP{}, samplePayload, time.Second); err != nil {
+ t.Fatalf("expected a packet with payload %v: %s", samplePayload, 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(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
+
+ startRTO := time.Second
+ current := startRTO
+ first := time.Now()
+ dut.Send(acceptFd, sampleData, 0)
+ seq := tb.Uint32(uint32(*conn.RemoteSeqNum()))
+ if _, err := conn.ExpectData(&tb.TCP{SeqNum: seq}, samplePayload, startRTO); err != nil {
+ t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
+ }
+ // Expect retransmits of the same segment.
+ for i := 0; i < 5; i++ {
+ start := time.Now()
+ if _, err := conn.ExpectData(&tb.TCP{SeqNum: seq}, samplePayload, 2*current); err != nil {
+ t.Fatalf("expected a packet with payload %v: %s loop %d", samplePayload, err, i)
+ }
+ if i == 0 {
+ startRTO = time.Now().Sub(first)
+ current = 2 * startRTO
+ continue
+ }
+ // Check if the probes came at exponentially increasing intervals.
+ if p := time.Since(start); p < current-startRTO {
+ t.Fatalf("retransmit came sooner interval %d probe %d\n", p, i)
+ }
+ current *= 2
+ }
+}
diff --git a/test/packetimpact/tests/tcp_should_piggyback_test.go b/test/packetimpact/tests/tcp_should_piggyback_test.go
index b0be6ba23..0240dc2f9 100644
--- a/test/packetimpact/tests/tcp_should_piggyback_test.go
+++ b/test/packetimpact/tests/tcp_should_piggyback_test.go
@@ -15,6 +15,7 @@
package tcp_should_piggyback_test
import (
+ "flag"
"testing"
"time"
@@ -23,6 +24,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
func TestPiggyback(t *testing.T) {
dut := tb.NewDUT(t)
defer dut.TearDown()
diff --git a/test/packetimpact/tests/tcp_user_timeout_test.go b/test/packetimpact/tests/tcp_user_timeout_test.go
index 3cf82badb..ce31917ee 100644
--- a/test/packetimpact/tests/tcp_user_timeout_test.go
+++ b/test/packetimpact/tests/tcp_user_timeout_test.go
@@ -15,6 +15,7 @@
package tcp_user_timeout_test
import (
+ "flag"
"fmt"
"testing"
"time"
@@ -24,6 +25,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
func sendPayload(conn *tb.TCPIPv4, dut *tb.DUT, fd int32) error {
sampleData := make([]byte, 100)
for i := range sampleData {
diff --git a/test/packetimpact/tests/tcp_window_shrink_test.go b/test/packetimpact/tests/tcp_window_shrink_test.go
index c9354074e..58ec1d740 100644
--- a/test/packetimpact/tests/tcp_window_shrink_test.go
+++ b/test/packetimpact/tests/tcp_window_shrink_test.go
@@ -15,6 +15,7 @@
package tcp_window_shrink_test
import (
+ "flag"
"testing"
"time"
@@ -23,6 +24,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
func TestWindowShrink(t *testing.T) {
dut := tb.NewDUT(t)
defer dut.TearDown()
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 864e5a634..dd43a24db 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 (
+ "flag"
"testing"
"time"
@@ -23,6 +24,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
// TestZeroWindowProbeRetransmit tests retransmits of zero window probes
// to be sent at exponentially inreasing time intervals.
func TestZeroWindowProbeRetransmit(t *testing.T) {
diff --git a/test/packetimpact/tests/tcp_zero_window_probe_test.go b/test/packetimpact/tests/tcp_zero_window_probe_test.go
index 4fa3d0cd4..6c453505d 100644
--- a/test/packetimpact/tests/tcp_zero_window_probe_test.go
+++ b/test/packetimpact/tests/tcp_zero_window_probe_test.go
@@ -15,6 +15,7 @@
package tcp_zero_window_probe_test
import (
+ "flag"
"testing"
"time"
@@ -23,6 +24,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
// TestZeroWindowProbe tests few cases of zero window probing over the
// same connection.
func TestZeroWindowProbe(t *testing.T) {
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 7d81c276c..193427fb9 100644
--- a/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go
+++ b/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go
@@ -15,6 +15,7 @@
package tcp_zero_window_probe_usertimeout_test
import (
+ "flag"
"testing"
"time"
@@ -23,6 +24,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
// TestZeroWindowProbeUserTimeout sanity tests user timeout when we are
// retransmitting zero window probes.
func TestZeroWindowProbeUserTimeout(t *testing.T) {
diff --git a/test/packetimpact/tests/udp_icmp_error_propagation_test.go b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
index 30dcb336e..ca4df2ab0 100644
--- a/test/packetimpact/tests/udp_icmp_error_propagation_test.go
+++ b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
@@ -16,8 +16,10 @@ package udp_icmp_error_propagation_test
import (
"context"
+ "flag"
"fmt"
"net"
+ "sync"
"syscall"
"testing"
"time"
@@ -27,6 +29,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
type connectionMode bool
func (c connectionMode) String() string {
@@ -300,19 +306,22 @@ func TestICMPErrorDuringUDPRecv(t *testing.T) {
t.Fatalf("did not receive message from DUT: %s", err)
}
- c := make(chan error)
- go func(c chan error) {
+ var wg sync.WaitGroup
+ wg.Add(2)
+ go func() {
+ defer wg.Done()
+
if wantErrno != syscall.Errno(0) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
ret, _, err := dut.RecvWithErrno(ctx, remoteFD, 100, 0)
if ret != -1 {
- c <- fmt.Errorf("recv during ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", wantErrno)
+ t.Errorf("recv during ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", wantErrno)
return
}
if err != wantErrno {
- c <- fmt.Errorf("recv during ICMP error resulted in error (%[1]d) %[1]v, expected (%[2]d) %[2]v", err, wantErrno)
+ t.Errorf("recv during ICMP error resulted in error (%[1]d) %[1]v, expected (%[2]d) %[2]v", err, wantErrno)
return
}
}
@@ -321,23 +330,20 @@ func TestICMPErrorDuringUDPRecv(t *testing.T) {
defer cancel()
if ret, _, err := dut.RecvWithErrno(ctx, remoteFD, 100, 0); ret == -1 {
- c <- fmt.Errorf("recv after ICMP error failed with (%[1]d) %[1]", err)
- return
+ t.Errorf("recv after ICMP error failed with (%[1]d) %[1]", err)
}
- c <- nil
- }(c)
+ }()
+
+ go func() {
+ defer wg.Done()
- cleanChan := make(chan error)
- go func(c chan error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if ret, _, err := dut.RecvWithErrno(ctx, cleanFD, 100, 0); ret == -1 {
- c <- fmt.Errorf("recv on clean socket failed with (%[1]d) %[1]", err)
- return
+ t.Errorf("recv on clean socket failed with (%[1]d) %[1]", err)
}
- c <- nil
- }(cleanChan)
+ }()
// TODO(b/155684889) This sleep is to allow time for the DUT to
// actually call recv since we want the ICMP error to arrive during the
@@ -351,14 +357,7 @@ func TestICMPErrorDuringUDPRecv(t *testing.T) {
conn.Send(tb.UDP{DstPort: &cleanPort})
conn.Send(tb.UDP{})
-
- err, errClean := <-c, <-cleanChan
- if errClean != nil {
- t.Error(err)
- }
- if err != nil {
- t.Fatal(err)
- }
+ wg.Wait()
})
}
}
diff --git a/test/packetimpact/tests/udp_recv_multicast_test.go b/test/packetimpact/tests/udp_recv_multicast_test.go
index 61fd17050..0bae18ba3 100644
--- a/test/packetimpact/tests/udp_recv_multicast_test.go
+++ b/test/packetimpact/tests/udp_recv_multicast_test.go
@@ -15,6 +15,7 @@
package udp_recv_multicast_test
import (
+ "flag"
"net"
"testing"
@@ -23,6 +24,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
func TestUDPRecvMulticast(t *testing.T) {
dut := tb.NewDUT(t)
defer dut.TearDown()
diff --git a/test/packetimpact/tests/udp_send_recv_dgram_test.go b/test/packetimpact/tests/udp_send_recv_dgram_test.go
index 1c682831c..350875a6f 100644
--- a/test/packetimpact/tests/udp_send_recv_dgram_test.go
+++ b/test/packetimpact/tests/udp_send_recv_dgram_test.go
@@ -15,6 +15,7 @@
package udp_send_recv_dgram_test
import (
+ "flag"
"math/rand"
"net"
"testing"
@@ -24,6 +25,10 @@ import (
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
+func init() {
+ tb.RegisterFlags(flag.CommandLine)
+}
+
func generateRandomPayload(t *testing.T, n int) string {
t.Helper()
buf := make([]byte, n)
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index f7c2eab85..5acdb8438 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -2617,6 +2617,7 @@ cc_binary(
":socket_bind_to_device_util",
":socket_test_util",
"//test/util:capability_util",
+ "@com_google_absl//absl/container:node_hash_map",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3284,6 +3285,7 @@ cc_binary(
"//test/util:capability_util",
"//test/util:file_descriptor",
"//test/util:fs_util",
+ "@com_google_absl//absl/time",
gtest,
"//test/util:temp_path",
"//test/util:test_main",
diff --git a/test/syscalls/linux/fpsig_fork.cc b/test/syscalls/linux/fpsig_fork.cc
index 62f2f7d23..d08111cd3 100644
--- a/test/syscalls/linux/fpsig_fork.cc
+++ b/test/syscalls/linux/fpsig_fork.cc
@@ -34,12 +34,14 @@ namespace {
#define GET_FP0(__var) GET_XMM(__var, xmm0)
#define SET_FP0(__var) SET_XMM(__var, xmm0)
#elif __aarch64__
-#define __stringify_1(x...) #x
-#define __stringify(x...) __stringify_1(x)
-#define GET_FPREG( var, regname ) asm volatile("str "__stringify(regname)", %0" : "=m" (var) )
-#define SET_FPREG( var, regname ) asm volatile("ldr "__stringify(regname)", %0" : "=m" (var) )
-#define GET_FP0( var ) GET_FPREG( var, d0 )
-#define SET_FP0( var ) GET_FPREG( var, d0 )
+#define __stringify_1(x...) #x
+#define __stringify(x...) __stringify_1(x)
+#define GET_FPREG(var, regname) \
+ asm volatile("str "__stringify(regname) ", %0" : "=m"(var))
+#define SET_FPREG(var, regname) \
+ asm volatile("ldr "__stringify(regname) ", %0" : "=m"(var))
+#define GET_FP0(var) GET_FPREG(var, d0)
+#define SET_FP0(var) GET_FPREG(var, d0)
#endif
int parent, child;
@@ -102,7 +104,8 @@ TEST(FPSigTest, Fork) {
"mov x0, %1\n"
"mov x1, %2\n"
"mov x2, %3\n"
- "svc #0\n"::"r"(__NR_tgkill), "r"(parent), "r"(parent_tid), "r"(SIGUSR1));
+ "svc #0\n" ::"r"(__NR_tgkill),
+ "r"(parent), "r"(parent_tid), "r"(SIGUSR1));
#endif
uint64_t got;
diff --git a/test/syscalls/linux/socket.cc b/test/syscalls/linux/socket.cc
index 703d594a2..afa59c1da 100644
--- a/test/syscalls/linux/socket.cc
+++ b/test/syscalls/linux/socket.cc
@@ -61,7 +61,7 @@ TEST(SocketTest, ProtocolInet) {
}
}
-TEST(SocketTest, UnixSocketFileMode) {
+TEST(SocketTest, UnixSocketStat) {
// TODO(gvisor.dev/issue/1624): Re-enable this test once VFS1 is deleted. It
// should pass in VFS2.
SKIP_IF(IsRunningOnGvisor());
@@ -83,7 +83,14 @@ TEST(SocketTest, UnixSocketFileMode) {
struct stat statbuf = {};
ASSERT_THAT(stat(addr.sun_path, &statbuf), SyscallSucceeds());
+
+ // Mode should be S_IFSOCK.
EXPECT_EQ(statbuf.st_mode, S_IFSOCK | sock_perm & ~mask);
+
+ // Timestamps should be equal and non-zero.
+ EXPECT_NE(statbuf.st_atime, 0);
+ EXPECT_EQ(statbuf.st_atime, statbuf.st_mtime);
+ EXPECT_EQ(statbuf.st_atime, statbuf.st_ctime);
}
TEST(SocketTest, UnixConnectNeedsWritePerm) {
diff --git a/test/syscalls/linux/socket_bind_to_device_sequence.cc b/test/syscalls/linux/socket_bind_to_device_sequence.cc
index 637d1151a..1967329ee 100644
--- a/test/syscalls/linux/socket_bind_to_device_sequence.cc
+++ b/test/syscalls/linux/socket_bind_to_device_sequence.cc
@@ -33,6 +33,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/container/node_hash_map.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_bind_to_device_util.h"
#include "test/syscalls/linux/socket_test_util.h"
@@ -192,8 +193,8 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> {
in_port_t port_ = 0;
// sockets_to_close_ is a map from action index to the socket that was
// created.
- std::unordered_map<int,
- std::unique_ptr<gvisor::testing::FileDescriptor>>
+ absl::node_hash_map<int,
+ std::unique_ptr<gvisor::testing::FileDescriptor>>
sockets_to_close_;
int next_socket_id_ = 0;
};
diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc
index 27779e47c..fa81845fd 100644
--- a/test/syscalls/linux/socket_ip_tcp_generic.cc
+++ b/test/syscalls/linux/socket_ip_tcp_generic.cc
@@ -876,6 +876,51 @@ TEST_P(TCPSocketPairTest, SetTCPUserTimeoutAboveZero) {
EXPECT_EQ(get, kAbove);
}
+TEST_P(TCPSocketPairTest, SetTCPWindowClampBelowMinRcvBufConnectedSocket) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ // Discover minimum receive buf by setting a really low value
+ // for the receive buffer.
+ constexpr int kZero = 0;
+ EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &kZero,
+ sizeof(kZero)),
+ SyscallSucceeds());
+
+ // Now retrieve the minimum value for SO_RCVBUF as the set above should
+ // have caused SO_RCVBUF for the socket to be set to the minimum.
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ int min_so_rcvbuf = get;
+
+ {
+ // Setting TCP_WINDOW_CLAMP to zero for a connected socket is not permitted.
+ constexpr int kZero = 0;
+ EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_WINDOW_CLAMP,
+ &kZero, sizeof(kZero)),
+ SyscallFailsWithErrno(EINVAL));
+
+ // Non-zero clamp values below MIN_SO_RCVBUF/2 should result in the clamp
+ // being set to MIN_SO_RCVBUF/2.
+ int below_half_min_so_rcvbuf = min_so_rcvbuf / 2 - 1;
+ EXPECT_THAT(
+ setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_WINDOW_CLAMP,
+ &below_half_min_so_rcvbuf, sizeof(below_half_min_so_rcvbuf)),
+ SyscallSucceeds());
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+
+ ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_WINDOW_CLAMP,
+ &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(min_so_rcvbuf / 2, get);
+ }
+}
+
TEST_P(TCPSocketPairTest, TCPResetDuringClose_NoRandomSave) {
DisableSave ds; // Too many syscalls.
constexpr int kThreadCount = 1000;
diff --git a/test/syscalls/linux/symlink.cc b/test/syscalls/linux/symlink.cc
index 03ee1250d..a17ff62e9 100644
--- a/test/syscalls/linux/symlink.cc
+++ b/test/syscalls/linux/symlink.cc
@@ -20,6 +20,7 @@
#include <string>
#include "gtest/gtest.h"
+#include "absl/time/clock.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
@@ -272,6 +273,30 @@ TEST(SymlinkTest, ChmodSymlink) {
EXPECT_EQ(FilePermission(newpath), 0777);
}
+// Test that following a symlink updates the atime on the symlink.
+TEST(SymlinkTest, FollowUpdatesATime) {
+ const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ const std::string link = NewTempAbsPath();
+ EXPECT_THAT(symlink(file.path().c_str(), link.c_str()), SyscallSucceeds());
+
+ // Lstat the symlink.
+ struct stat st_before_follow;
+ ASSERT_THAT(lstat(link.c_str(), &st_before_follow), SyscallSucceeds());
+
+ // Let the clock advance.
+ absl::SleepFor(absl::Seconds(1));
+
+ // Open the file via the symlink.
+ int fd;
+ ASSERT_THAT(fd = open(link.c_str(), O_RDWR, 0666), SyscallSucceeds());
+ FileDescriptor fd_closer(fd);
+
+ // Lstat the symlink again, and check that atime is updated.
+ struct stat st_after_follow;
+ ASSERT_THAT(lstat(link.c_str(), &st_after_follow), SyscallSucceeds());
+ EXPECT_LT(st_before_follow.st_atime, st_after_follow.st_atime);
+}
+
class ParamSymlinkTest : public ::testing::TestWithParam<std::string> {};
// Test that creating an existing symlink with creat will create the target.
diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc
index d9c1ac0e1..a4d2953e1 100644
--- a/test/syscalls/linux/tcp_socket.cc
+++ b/test/syscalls/linux/tcp_socket.cc
@@ -1313,7 +1313,7 @@ TEST_P(SimpleTcpSocketTest, SetTCPDeferAcceptNeg) {
int get = -1;
socklen_t get_len = sizeof(get);
ASSERT_THAT(
- getsockopt(s.get(), IPPROTO_TCP, TCP_USER_TIMEOUT, &get, &get_len),
+ getsockopt(s.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT, &get, &get_len),
SyscallSucceedsWithValue(0));
EXPECT_EQ(get_len, sizeof(get));
EXPECT_EQ(get, 0);
@@ -1326,7 +1326,7 @@ TEST_P(SimpleTcpSocketTest, GetTCPDeferAcceptDefault) {
int get = -1;
socklen_t get_len = sizeof(get);
ASSERT_THAT(
- getsockopt(s.get(), IPPROTO_TCP, TCP_USER_TIMEOUT, &get, &get_len),
+ getsockopt(s.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT, &get, &get_len),
SyscallSucceedsWithValue(0));
EXPECT_EQ(get_len, sizeof(get));
EXPECT_EQ(get, 0);
@@ -1378,6 +1378,187 @@ TEST_P(SimpleTcpSocketTest, TCPConnectSoRcvBufRace) {
SyscallSucceedsWithValue(0));
}
+TEST_P(SimpleTcpSocketTest, SetTCPSynCntLessThanOne) {
+ FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ int default_syn_cnt = get;
+
+ {
+ // TCP_SYNCNT less than 1 should be rejected with an EINVAL.
+ constexpr int kZero = 0;
+ EXPECT_THAT(
+ setsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &kZero, sizeof(kZero)),
+ SyscallFailsWithErrno(EINVAL));
+
+ // TCP_SYNCNT less than 1 should be rejected with an EINVAL.
+ constexpr int kNeg = -1;
+ EXPECT_THAT(
+ setsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &kNeg, sizeof(kNeg)),
+ SyscallFailsWithErrno(EINVAL));
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+
+ ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(default_syn_cnt, get);
+ }
+}
+
+TEST_P(SimpleTcpSocketTest, GetTCPSynCntDefault) {
+ FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ constexpr int kDefaultSynCnt = 6;
+
+ ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(get, kDefaultSynCnt);
+}
+
+TEST_P(SimpleTcpSocketTest, SetTCPSynCntGreaterThanOne) {
+ FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+ constexpr int kTCPSynCnt = 20;
+ ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &kTCPSynCnt,
+ sizeof(kTCPSynCnt)),
+ SyscallSucceeds());
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &get, &get_len),
+ SyscallSucceeds());
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(get, kTCPSynCnt);
+}
+
+TEST_P(SimpleTcpSocketTest, SetTCPSynCntAboveMax) {
+ FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ int default_syn_cnt = get;
+ {
+ constexpr int kTCPSynCnt = 256;
+ ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &kTCPSynCnt,
+ sizeof(kTCPSynCnt)),
+ SyscallFailsWithErrno(EINVAL));
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &get, &get_len),
+ SyscallSucceeds());
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(get, default_syn_cnt);
+ }
+}
+
+TEST_P(SimpleTcpSocketTest, SetTCPWindowClampBelowMinRcvBuf) {
+ FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ // Discover minimum receive buf by setting a really low value
+ // for the receive buffer.
+ constexpr int kZero = 0;
+ EXPECT_THAT(setsockopt(s.get(), SOL_SOCKET, SO_RCVBUF, &kZero, sizeof(kZero)),
+ SyscallSucceeds());
+
+ // Now retrieve the minimum value for SO_RCVBUF as the set above should
+ // have caused SO_RCVBUF for the socket to be set to the minimum.
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_RCVBUF, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ int min_so_rcvbuf = get;
+
+ {
+ // TCP_WINDOW_CLAMP less than min_so_rcvbuf/2 should be set to
+ // min_so_rcvbuf/2.
+ int below_half_min_rcvbuf = min_so_rcvbuf / 2 - 1;
+ EXPECT_THAT(
+ setsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP,
+ &below_half_min_rcvbuf, sizeof(below_half_min_rcvbuf)),
+ SyscallSucceeds());
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+
+ ASSERT_THAT(
+ getsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(min_so_rcvbuf / 2, get);
+ }
+}
+
+TEST_P(SimpleTcpSocketTest, SetTCPWindowClampZeroClosedSocket) {
+ FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+ constexpr int kZero = 0;
+ ASSERT_THAT(
+ setsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP, &kZero, sizeof(kZero)),
+ SyscallSucceeds());
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP, &get, &get_len),
+ SyscallSucceeds());
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(get, kZero);
+}
+
+TEST_P(SimpleTcpSocketTest, SetTCPWindowClampAboveHalfMinRcvBuf) {
+ FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ // Discover minimum receive buf by setting a really low value
+ // for the receive buffer.
+ constexpr int kZero = 0;
+ EXPECT_THAT(setsockopt(s.get(), SOL_SOCKET, SO_RCVBUF, &kZero, sizeof(kZero)),
+ SyscallSucceeds());
+
+ // Now retrieve the minimum value for SO_RCVBUF as the set above should
+ // have caused SO_RCVBUF for the socket to be set to the minimum.
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_RCVBUF, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ int min_so_rcvbuf = get;
+
+ {
+ int above_half_min_rcv_buf = min_so_rcvbuf / 2 + 1;
+ EXPECT_THAT(
+ setsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP,
+ &above_half_min_rcv_buf, sizeof(above_half_min_rcv_buf)),
+ SyscallSucceeds());
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+
+ ASSERT_THAT(
+ getsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(above_half_min_rcv_buf, get);
+ }
+}
+
INSTANTIATE_TEST_SUITE_P(AllInetTests, SimpleTcpSocketTest,
::testing::Values(AF_INET, AF_INET6));