diff options
Diffstat (limited to 'test/packetimpact')
42 files changed, 711 insertions, 308 deletions
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) } } |