summaryrefslogtreecommitdiffhomepage
path: root/test/packetimpact
diff options
context:
space:
mode:
Diffstat (limited to 'test/packetimpact')
-rw-r--r--test/packetimpact/dut/posix_server.cc35
-rw-r--r--test/packetimpact/proto/posix_server.proto29
-rw-r--r--test/packetimpact/runner/defs.bzl15
-rw-r--r--test/packetimpact/testbench/dut.go106
-rw-r--r--test/packetimpact/tests/BUILD14
-rw-r--r--test/packetimpact/tests/tcp_queue_receive_in_syn_sent_test.go130
-rw-r--r--test/packetimpact/tests/tcp_queue_send_in_syn_sent_test.go131
-rw-r--r--test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go287
8 files changed, 379 insertions, 368 deletions
diff --git a/test/packetimpact/dut/posix_server.cc b/test/packetimpact/dut/posix_server.cc
index eba21df12..0d93b806e 100644
--- a/test/packetimpact/dut/posix_server.cc
+++ b/test/packetimpact/dut/posix_server.cc
@@ -180,16 +180,6 @@ class PosixImpl final : public posix_server::Posix::Service {
return ::grpc::Status::OK;
}
- ::grpc::Status Fcntl(grpc::ServerContext *context,
- const ::posix_server::FcntlRequest *request,
- ::posix_server::FcntlResponse *response) override {
- response->set_ret(::fcntl(request->fd(), request->cmd(), request->arg()));
- if (response->ret() < 0) {
- response->set_errno_(errno);
- }
- return ::grpc::Status::OK;
- }
-
::grpc::Status GetSockName(
grpc::ServerContext *context,
const ::posix_server::GetSockNameRequest *request,
@@ -330,6 +320,31 @@ class PosixImpl final : public posix_server::Posix::Service {
return ::grpc::Status::OK;
}
+ ::grpc::Status SetNonblocking(
+ grpc::ServerContext *context,
+ const ::posix_server::SetNonblockingRequest *request,
+ ::posix_server::SetNonblockingResponse *response) override {
+ int flags = fcntl(request->fd(), F_GETFL);
+ if (flags == -1) {
+ response->set_ret(-1);
+ response->set_errno_(errno);
+ response->set_cmd("F_GETFL");
+ return ::grpc::Status::OK;
+ }
+ if (request->nonblocking()) {
+ flags |= O_NONBLOCK;
+ } else {
+ flags &= ~O_NONBLOCK;
+ }
+ int ret = fcntl(request->fd(), F_SETFL, flags);
+ response->set_ret(ret);
+ if (ret == -1) {
+ response->set_errno_(errno);
+ response->set_cmd("F_SETFL");
+ }
+ return ::grpc::Status::OK;
+ }
+
::grpc::Status SetSockOpt(
grpc::ServerContext *context,
const ::posix_server::SetSockOptRequest *request,
diff --git a/test/packetimpact/proto/posix_server.proto b/test/packetimpact/proto/posix_server.proto
index b4c68764a..521f03465 100644
--- a/test/packetimpact/proto/posix_server.proto
+++ b/test/packetimpact/proto/posix_server.proto
@@ -91,17 +91,6 @@ message ConnectResponse {
int32 errno_ = 2; // "errno" may fail to compile in c++.
}
-message FcntlRequest {
- int32 fd = 1;
- int32 cmd = 2;
- int32 arg = 3;
-}
-
-message FcntlResponse {
- int32 ret = 1;
- int32 errno_ = 2;
-}
-
message GetSockNameRequest {
int32 sockfd = 1;
}
@@ -184,6 +173,18 @@ message SendToResponse {
int32 errno_ = 2; // "errno" may fail to compile in c++.
}
+message SetNonblockingRequest {
+ int32 fd = 1;
+ bool nonblocking = 2;
+}
+
+message SetNonblockingResponse {
+ int32 ret = 1;
+ int32 errno_ = 2; // "errno" may fail to compile in c++.
+ // The failed fcntl cmd.
+ string cmd = 3;
+}
+
message SetSockOptRequest {
int32 sockfd = 1;
int32 level = 2;
@@ -237,8 +238,6 @@ service Posix {
rpc Close(CloseRequest) returns (CloseResponse);
// Call connect() on the DUT.
rpc Connect(ConnectRequest) returns (ConnectResponse);
- // Call fcntl() on the DUT.
- rpc Fcntl(FcntlRequest) returns (FcntlResponse);
// Call getsockname() on the DUT.
rpc GetSockName(GetSockNameRequest) returns (GetSockNameResponse);
// Call getsockopt() on the DUT.
@@ -253,6 +252,10 @@ service Posix {
rpc Send(SendRequest) returns (SendResponse);
// Call sendto() on the DUT.
rpc SendTo(SendToRequest) returns (SendToResponse);
+ // Set/Clear O_NONBLOCK flag on the requested fd. This is needed because the
+ // operating system on DUT may have a different definition for O_NONBLOCK, it
+ // is not sound to assemble flags on testbench.
+ rpc SetNonblocking(SetNonblockingRequest) returns (SetNonblockingResponse);
// Call setsockopt() on the DUT.
rpc SetSockOpt(SetSockOptRequest) returns (SetSockOptResponse);
// Call socket() on the DUT.
diff --git a/test/packetimpact/runner/defs.bzl b/test/packetimpact/runner/defs.bzl
index 52ed5ae39..8ce5edf2b 100644
--- a/test/packetimpact/runner/defs.bzl
+++ b/test/packetimpact/runner/defs.bzl
@@ -128,14 +128,14 @@ def packetimpact_go_test(name, expect_native_failure = False, expect_netstack_fa
packetimpact_native_test(
name = name,
expect_failure = expect_native_failure,
- testbench_binary = testbench_binary,
num_duts = num_duts,
+ testbench_binary = testbench_binary,
)
packetimpact_netstack_test(
name = name,
expect_failure = expect_netstack_failure,
- testbench_binary = testbench_binary,
num_duts = num_duts,
+ testbench_binary = testbench_binary,
)
def packetimpact_testbench(name, size = "small", pure = True, **kwargs):
@@ -161,7 +161,11 @@ def packetimpact_testbench(name, size = "small", pure = True, **kwargs):
PacketimpactTestInfo = provider(
doc = "Provide information for packetimpact tests",
- fields = ["name", "expect_netstack_failure", "num_duts"],
+ fields = [
+ "name",
+ "expect_netstack_failure",
+ "num_duts",
+ ],
)
ALL_TESTS = [
@@ -219,7 +223,7 @@ ALL_TESTS = [
name = "tcp_zero_receive_window",
),
PacketimpactTestInfo(
- name = "tcp_queue_receive_in_syn_sent",
+ name = "tcp_queue_send_recv_in_syn_sent",
),
PacketimpactTestInfo(
name = "tcp_synsent_reset",
@@ -242,9 +246,6 @@ ALL_TESTS = [
expect_netstack_failure = True,
),
PacketimpactTestInfo(
- name = "tcp_queue_send_in_syn_sent",
- ),
- PacketimpactTestInfo(
name = "icmpv6_param_problem",
),
PacketimpactTestInfo(
diff --git a/test/packetimpact/testbench/dut.go b/test/packetimpact/testbench/dut.go
index 81634b5f4..be5121d98 100644
--- a/test/packetimpact/testbench/dut.go
+++ b/test/packetimpact/testbench/dut.go
@@ -189,10 +189,10 @@ func (dut *DUT) Accept(t *testing.T, sockfd int32) (int32, unix.Sockaddr) {
func (dut *DUT) AcceptWithErrno(ctx context.Context, t *testing.T, sockfd int32) (int32, unix.Sockaddr, error) {
t.Helper()
- req := pb.AcceptRequest{
+ req := &pb.AcceptRequest{
Sockfd: sockfd,
}
- resp, err := dut.posixServer.Accept(ctx, &req)
+ resp, err := dut.posixServer.Accept(ctx, req)
if err != nil {
t.Fatalf("failed to call Accept: %s", err)
}
@@ -217,11 +217,11 @@ func (dut *DUT) Bind(t *testing.T, fd int32, sa unix.Sockaddr) {
func (dut *DUT) BindWithErrno(ctx context.Context, t *testing.T, fd int32, sa unix.Sockaddr) (int32, error) {
t.Helper()
- req := pb.BindRequest{
+ req := &pb.BindRequest{
Sockfd: fd,
Addr: dut.sockaddrToProto(t, sa),
}
- resp, err := dut.posixServer.Bind(ctx, &req)
+ resp, err := dut.posixServer.Bind(ctx, req)
if err != nil {
t.Fatalf("failed to call Bind: %s", err)
}
@@ -246,10 +246,10 @@ func (dut *DUT) Close(t *testing.T, fd int32) {
func (dut *DUT) CloseWithErrno(ctx context.Context, t *testing.T, fd int32) (int32, error) {
t.Helper()
- req := pb.CloseRequest{
+ req := &pb.CloseRequest{
Fd: fd,
}
- resp, err := dut.posixServer.Close(ctx, &req)
+ resp, err := dut.posixServer.Close(ctx, req)
if err != nil {
t.Fatalf("failed to call Close: %s", err)
}
@@ -276,48 +276,17 @@ func (dut *DUT) Connect(t *testing.T, fd int32, sa unix.Sockaddr) {
func (dut *DUT) ConnectWithErrno(ctx context.Context, t *testing.T, fd int32, sa unix.Sockaddr) (int32, error) {
t.Helper()
- req := pb.ConnectRequest{
+ req := &pb.ConnectRequest{
Sockfd: fd,
Addr: dut.sockaddrToProto(t, sa),
}
- resp, err := dut.posixServer.Connect(ctx, &req)
+ resp, err := dut.posixServer.Connect(ctx, req)
if err != nil {
t.Fatalf("failed to call Connect: %s", err)
}
return resp.GetRet(), syscall.Errno(resp.GetErrno_())
}
-// Fcntl calls fcntl 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 FcntlWithErrno.
-func (dut *DUT) Fcntl(t *testing.T, fd, cmd, arg int32) int32 {
- t.Helper()
-
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.FcntlWithErrno(ctx, t, fd, cmd, arg)
- if ret == -1 {
- t.Fatalf("failed to Fcntl: ret=%d, errno=%s", ret, err)
- }
- return ret
-}
-
-// FcntlWithErrno calls fcntl on the DUT.
-func (dut *DUT) FcntlWithErrno(ctx context.Context, t *testing.T, fd, cmd, arg int32) (int32, error) {
- t.Helper()
-
- req := pb.FcntlRequest{
- Fd: fd,
- Cmd: cmd,
- Arg: arg,
- }
- resp, err := dut.posixServer.Fcntl(ctx, &req)
- if err != nil {
- t.Fatalf("failed to call Fcntl: %s", err)
- }
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
-}
-
// GetSockName calls getsockname 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 GetSockNameWithErrno.
@@ -337,10 +306,10 @@ func (dut *DUT) GetSockName(t *testing.T, sockfd int32) unix.Sockaddr {
func (dut *DUT) GetSockNameWithErrno(ctx context.Context, t *testing.T, sockfd int32) (int32, unix.Sockaddr, error) {
t.Helper()
- req := pb.GetSockNameRequest{
+ req := &pb.GetSockNameRequest{
Sockfd: sockfd,
}
- resp, err := dut.posixServer.GetSockName(ctx, &req)
+ resp, err := dut.posixServer.GetSockName(ctx, req)
if err != nil {
t.Fatalf("failed to call Bind: %s", err)
}
@@ -350,14 +319,14 @@ func (dut *DUT) GetSockNameWithErrno(ctx context.Context, t *testing.T, sockfd i
func (dut *DUT) getSockOpt(ctx context.Context, t *testing.T, sockfd, level, optname, optlen int32, typ pb.GetSockOptRequest_SockOptType) (int32, *pb.SockOptVal, error) {
t.Helper()
- req := pb.GetSockOptRequest{
+ req := &pb.GetSockOptRequest{
Sockfd: sockfd,
Level: level,
Optname: optname,
Optlen: optlen,
Type: typ,
}
- resp, err := dut.posixServer.GetSockOpt(ctx, &req)
+ resp, err := dut.posixServer.GetSockOpt(ctx, req)
if err != nil {
t.Fatalf("failed to call GetSockOpt: %s", err)
}
@@ -475,11 +444,11 @@ func (dut *DUT) Listen(t *testing.T, sockfd, backlog int32) {
func (dut *DUT) ListenWithErrno(ctx context.Context, t *testing.T, sockfd, backlog int32) (int32, error) {
t.Helper()
- req := pb.ListenRequest{
+ req := &pb.ListenRequest{
Sockfd: sockfd,
Backlog: backlog,
}
- resp, err := dut.posixServer.Listen(ctx, &req)
+ resp, err := dut.posixServer.Listen(ctx, req)
if err != nil {
t.Fatalf("failed to call Listen: %s", err)
}
@@ -527,7 +496,7 @@ func (dut *DUT) Poll(t *testing.T, pfds []unix.PollFd, timeout time.Duration) []
func (dut *DUT) PollWithErrno(ctx context.Context, t *testing.T, pfds []unix.PollFd, timeout time.Duration) (int32, []unix.PollFd, error) {
t.Helper()
- req := pb.PollRequest{
+ req := &pb.PollRequest{
TimeoutMillis: int32(timeout.Milliseconds()),
}
for _, pfd := range pfds {
@@ -536,7 +505,7 @@ func (dut *DUT) PollWithErrno(ctx context.Context, t *testing.T, pfds []unix.Pol
Events: uint32(pfd.Events),
})
}
- resp, err := dut.posixServer.Poll(ctx, &req)
+ resp, err := dut.posixServer.Poll(ctx, req)
if err != nil {
t.Fatalf("failed to call Poll: %s", err)
}
@@ -572,12 +541,12 @@ func (dut *DUT) Send(t *testing.T, sockfd int32, buf []byte, flags int32) int32
func (dut *DUT) SendWithErrno(ctx context.Context, t *testing.T, sockfd int32, buf []byte, flags int32) (int32, error) {
t.Helper()
- req := pb.SendRequest{
+ req := &pb.SendRequest{
Sockfd: sockfd,
Buf: buf,
Flags: flags,
}
- resp, err := dut.posixServer.Send(ctx, &req)
+ resp, err := dut.posixServer.Send(ctx, req)
if err != nil {
t.Fatalf("failed to call Send: %s", err)
}
@@ -603,13 +572,13 @@ func (dut *DUT) SendTo(t *testing.T, sockfd int32, buf []byte, flags int32, dest
func (dut *DUT) SendToWithErrno(ctx context.Context, t *testing.T, sockfd int32, buf []byte, flags int32, destAddr unix.Sockaddr) (int32, error) {
t.Helper()
- req := pb.SendToRequest{
+ req := &pb.SendToRequest{
Sockfd: sockfd,
Buf: buf,
Flags: flags,
DestAddr: dut.sockaddrToProto(t, destAddr),
}
- resp, err := dut.posixServer.SendTo(ctx, &req)
+ resp, err := dut.posixServer.SendTo(ctx, req)
if err != nil {
t.Fatalf("failed to call SendTo: %s", err)
}
@@ -621,25 +590,32 @@ func (dut *DUT) SendToWithErrno(ctx context.Context, t *testing.T, sockfd int32,
func (dut *DUT) SetNonBlocking(t *testing.T, fd int32, nonblocking bool) {
t.Helper()
- flags := dut.Fcntl(t, fd, unix.F_GETFL, 0)
- if nonblocking {
- flags |= unix.O_NONBLOCK
- } else {
- flags &= ^unix.O_NONBLOCK
+ req := &pb.SetNonblockingRequest{
+ Fd: fd,
+ Nonblocking: nonblocking,
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
+ defer cancel()
+
+ resp, err := dut.posixServer.SetNonblocking(ctx, req)
+ if err != nil {
+ 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_()))
}
- dut.Fcntl(t, fd, unix.F_SETFL, flags)
}
func (dut *DUT) setSockOpt(ctx context.Context, t *testing.T, sockfd, level, optname int32, optval *pb.SockOptVal) (int32, error) {
t.Helper()
- req := pb.SetSockOptRequest{
+ req := &pb.SetSockOptRequest{
Sockfd: sockfd,
Level: level,
Optname: optname,
Optval: optval,
}
- resp, err := dut.posixServer.SetSockOpt(ctx, &req)
+ resp, err := dut.posixServer.SetSockOpt(ctx, req)
if err != nil {
t.Fatalf("failed to call SetSockOpt: %s", err)
}
@@ -734,13 +710,13 @@ func (dut *DUT) Socket(t *testing.T, domain, typ, proto int32) int32 {
func (dut *DUT) SocketWithErrno(t *testing.T, domain, typ, proto int32) (int32, error) {
t.Helper()
- req := pb.SocketRequest{
+ req := &pb.SocketRequest{
Domain: domain,
Type: typ,
Protocol: proto,
}
ctx := context.Background()
- resp, err := dut.posixServer.Socket(ctx, &req)
+ resp, err := dut.posixServer.Socket(ctx, req)
if err != nil {
t.Fatalf("failed to call Socket: %s", err)
}
@@ -766,12 +742,12 @@ func (dut *DUT) Recv(t *testing.T, sockfd, len, flags int32) []byte {
func (dut *DUT) RecvWithErrno(ctx context.Context, t *testing.T, sockfd, len, flags int32) (int32, []byte, error) {
t.Helper()
- req := pb.RecvRequest{
+ req := &pb.RecvRequest{
Sockfd: sockfd,
Len: len,
Flags: flags,
}
- resp, err := dut.posixServer.Recv(ctx, &req)
+ resp, err := dut.posixServer.Recv(ctx, req)
if err != nil {
t.Fatalf("failed to call Recv: %s", err)
}
@@ -807,11 +783,11 @@ func (dut *DUT) Shutdown(t *testing.T, fd, how int32) error {
func (dut *DUT) ShutdownWithErrno(ctx context.Context, t *testing.T, fd, how int32) error {
t.Helper()
- req := pb.ShutdownRequest{
+ req := &pb.ShutdownRequest{
Fd: fd,
How: how,
}
- resp, err := dut.posixServer.Shutdown(ctx, &req)
+ resp, err := dut.posixServer.Shutdown(ctx, req)
if err != nil {
t.Fatalf("failed to call Shutdown: %s", err)
}
diff --git a/test/packetimpact/tests/BUILD b/test/packetimpact/tests/BUILD
index 3e27f50dc..301cf4980 100644
--- a/test/packetimpact/tests/BUILD
+++ b/test/packetimpact/tests/BUILD
@@ -176,8 +176,8 @@ packetimpact_testbench(
)
packetimpact_testbench(
- name = "tcp_queue_receive_in_syn_sent",
- srcs = ["tcp_queue_receive_in_syn_sent_test.go"],
+ name = "tcp_queue_send_recv_in_syn_sent",
+ srcs = ["tcp_queue_send_recv_in_syn_sent_test.go"],
deps = [
"//pkg/tcpip/header",
"//test/packetimpact/testbench",
@@ -246,16 +246,6 @@ packetimpact_testbench(
)
packetimpact_testbench(
- name = "tcp_queue_send_in_syn_sent",
- srcs = ["tcp_queue_send_in_syn_sent_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
name = "icmpv6_param_problem",
srcs = ["icmpv6_param_problem_test.go"],
deps = [
diff --git a/test/packetimpact/tests/tcp_queue_receive_in_syn_sent_test.go b/test/packetimpact/tests/tcp_queue_receive_in_syn_sent_test.go
deleted file mode 100644
index 646c93216..000000000
--- a/test/packetimpact/tests/tcp_queue_receive_in_syn_sent_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package tcp_queue_receive_in_syn_sent_test
-
-import (
- "bytes"
- "context"
- "encoding/hex"
- "errors"
- "flag"
- "sync"
- "syscall"
- "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)
-}
-
-// TestQueueReceiveInSynSent tests receive behavior when the TCP state
-// is SYN-SENT.
-// It tests for 2 variants where the receive is blocked and:
-// (1) we complete handshake and send sample data.
-// (2) we send a TCP RST.
-func TestQueueReceiveInSynSent(t *testing.T) {
- for _, tt := range []struct {
- description string
- reset bool
- }{
- {description: "Send DATA", reset: false},
- {description: "Send RST", reset: true},
- } {
- t.Run(tt.description, func(t *testing.T) {
- dut := testbench.NewDUT(t)
-
- socket, remotePort := dut.CreateBoundSocket(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(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) {
- 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 {
- 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)
- }
-
- // Test blocking read.
- dut.SetNonBlocking(t, socket, false)
-
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
- go func() {
- defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
-
- block.Done()
- // Issue RECEIVE call in SYN-SENT, this should be queued for
- // process until the connection is established.
- n, buff, err := dut.RecvWithErrno(ctx, t, socket, int32(len(sampleData)), 0)
- if tt.reset {
- if err != syscall.Errno(unix.ECONNREFUSED) {
- t.Errorf("expected error %s, got %s", syscall.Errno(unix.ECONNREFUSED), err)
- }
- if n != -1 {
- t.Errorf("expected return value %d, got %d", -1, n)
- }
- return
- }
- if n == -1 {
- t.Errorf("failed to recv on DUT: %s", err)
- }
- if got := buff[:n]; !bytes.Equal(got, sampleData) {
- t.Errorf("received data doesn't match, got:\n%s, want:\n%s", hex.Dump(got), hex.Dump(sampleData))
- }
- }()
-
- // Wait for the goroutine to be scheduled and before it
- // blocks on endpoint receive.
- block.Wait()
- // The following sleep is used to prevent the connection
- // from being established before we are blocked on Recv.
- time.Sleep(100 * time.Millisecond)
-
- if tt.reset {
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)})
- return
- }
-
- // 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 {
- t.Fatalf("expected an ACK from DUT, but got none: %s", err)
- }
-
- // Send sample payload and expect an ACK.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected an ACK from DUT, but got none: %s", err)
- }
- })
- }
-}
diff --git a/test/packetimpact/tests/tcp_queue_send_in_syn_sent_test.go b/test/packetimpact/tests/tcp_queue_send_in_syn_sent_test.go
deleted file mode 100644
index 29e51cae3..000000000
--- a/test/packetimpact/tests/tcp_queue_send_in_syn_sent_test.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package tcp_queue_send_in_syn_sent_test
-
-import (
- "context"
- "errors"
- "flag"
- "sync"
- "syscall"
- "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)
-}
-
-// TestQueueSendInSynSent tests send behavior when the TCP state
-// is SYN-SENT.
-// It tests for 2 variants when in SYN_SENT state and:
-// (1) DUT blocks on send and complete handshake
-// (2) DUT blocks on send and receive a TCP RST.
-func TestQueueSendInSynSent(t *testing.T) {
- for _, tt := range []struct {
- description string
- reset bool
- }{
- {description: "Complete handshake", reset: false},
- {description: "Send RST", reset: true},
- } {
- t.Run(tt.description, func(t *testing.T) {
- dut := testbench.NewDUT(t)
-
- socket, remotePort := dut.CreateBoundSocket(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- sampleData := []byte("Sample Data")
- samplePayload := &testbench.Payload{Bytes: sampleData}
- dut.SetNonBlocking(t, socket, true)
- if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, syscall.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 {
- t.Fatalf("expected a SYN from DUT, but got none: %s", err)
- }
- if _, err := dut.SendWithErrno(context.Background(), t, socket, sampleData, 0); err != syscall.Errno(unix.EWOULDBLOCK) {
- t.Fatalf("expected error %s, got %s", syscall.Errno(unix.EWOULDBLOCK), err)
- }
-
- // Test blocking write.
- dut.SetNonBlocking(t, socket, false)
-
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
- go func() {
- defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
-
- block.Done()
- // 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 tt.reset {
- if err != syscall.Errno(unix.ECONNREFUSED) {
- t.Errorf("expected error %s, got %s", syscall.Errno(unix.ECONNREFUSED), err)
- }
- if n != -1 {
- t.Errorf("expected return value %d, got %d", -1, n)
- }
- return
- }
- if n != int32(len(sampleData)) {
- t.Errorf("failed to send on DUT: %s", err)
- }
- }()
-
- // Wait for the goroutine to be scheduled and before it
- // blocks on endpoint send.
- block.Wait()
- // The following sleep is used to prevent the connection
- // from being established before we are blocked on send.
- time.Sleep(100 * time.Millisecond)
-
- if tt.reset {
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)})
- return
- }
-
- // Bring the connection to Established.
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(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)}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-
- // Send sample payload and expect an ACK to ensure connection is still ESTABLISHED.
- 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 {
- t.Fatalf("expected an ACK from DUT, but got none: %s", 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
new file mode 100644
index 000000000..7dd1c326a
--- /dev/null
+++ b/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
@@ -0,0 +1,287 @@
+// 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_queue_send_recv_in_syn_sent_test
+
+import (
+ "bytes"
+ "context"
+ "encoding/hex"
+ "errors"
+ "flag"
+ "sync"
+ "syscall"
+ "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)
+}
+
+// TestQueueSendInSynSentHandshake tests send behavior when the TCP state
+// is SYN-SENT and the connections is finally established.
+func TestQueueSendInSynSentHandshake(t *testing.T) {
+ dut := testbench.NewDUT(t)
+ socket, remotePort := dut.CreateBoundSocket(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
+ conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ defer conn.Close(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) {
+ 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 {
+ t.Fatalf("expected a SYN from DUT, but got none: %s", err)
+ }
+
+ // Test blocking send.
+ dut.SetNonBlocking(t, socket, false)
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+ wg.Add(1)
+ var block sync.WaitGroup
+ block.Add(1)
+ go func() {
+ defer wg.Done()
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
+ defer cancel()
+
+ block.Done()
+ // 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 n == -1 {
+ t.Errorf("failed to send on DUT: %s", err)
+ return
+ }
+ }()
+
+ // Wait for the goroutine to be scheduled and before it
+ // blocks on endpoint send/receive.
+ block.Wait()
+ // The following sleep is used to prevent the connection
+ // from being established before we are blocked: there is
+ // still a small time window between we sending the RPC
+ // request and the system actually being blocked.
+ time.Sleep(100 * time.Millisecond)
+
+ // Bring the connection to Established.
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(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 {
+ 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 {
+ t.Fatalf("expected an ACK from DUT, but got none: %s", err)
+ }
+}
+
+// TestQueueRecvInSynSentHandshake tests recv behavior when the TCP state
+// is SYN-SENT and the connections is finally established.
+func TestQueueRecvInSynSentHandshake(t *testing.T) {
+ dut := testbench.NewDUT(t)
+ socket, remotePort := dut.CreateBoundSocket(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
+ conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ defer conn.Close(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) {
+ 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 {
+ 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)
+ }
+
+ // Test blocking read.
+ dut.SetNonBlocking(t, socket, false)
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+ wg.Add(1)
+ var block sync.WaitGroup
+ block.Add(1)
+ go func() {
+ defer wg.Done()
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
+ defer cancel()
+
+ block.Done()
+ // Issue RECEIVE call in SYN-SENT, this should be queued for
+ // process until the connection is established.
+ n, buff, err := dut.RecvWithErrno(ctx, t, socket, int32(len(sampleData)), 0)
+ if n == -1 {
+ t.Errorf("failed to recv on DUT: %s", err)
+ return
+ }
+ if got := buff[:n]; !bytes.Equal(got, sampleData) {
+ t.Errorf("received data doesn't match, got:\n%s, want:\n%s", hex.Dump(got), hex.Dump(sampleData))
+ }
+ }()
+
+ // Wait for the goroutine to be scheduled and before it
+ // blocks on endpoint send/receive.
+ block.Wait()
+ // The following sleep is used to prevent the connection
+ // from being established before we are blocked: there is
+ // still a small time window between we sending the RPC
+ // request and the system actually being blocked.
+ 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 {
+ 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 {
+ t.Fatalf("expected an ACK from DUT, but got none: %s", err)
+ }
+}
+
+// TestQueueSendInSynSentRST tests send behavior when the TCP state
+// is SYN-SENT and an RST is sent.
+func TestQueueSendInSynSentRST(t *testing.T) {
+ dut := testbench.NewDUT(t)
+ socket, remotePort := dut.CreateBoundSocket(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
+ conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ defer conn.Close(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) {
+ 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 {
+ t.Fatalf("expected a SYN from DUT, but got none: %s", err)
+ }
+
+ // Test blocking send.
+ dut.SetNonBlocking(t, socket, false)
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+ wg.Add(1)
+ var block sync.WaitGroup
+ block.Add(1)
+ go func() {
+ defer wg.Done()
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
+ defer cancel()
+
+ block.Done()
+ // 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 n != -1 {
+ t.Errorf("expected return value %d, got %d", -1, n)
+ }
+ }()
+
+ // Wait for the goroutine to be scheduled and before it
+ // blocks on endpoint send/receive.
+ block.Wait()
+ // The following sleep is used to prevent the connection
+ // from being established before we are blocked: there is
+ // still a small time window between we sending the RPC
+ // request and the system actually being blocked.
+ time.Sleep(100 * time.Millisecond)
+
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)})
+}
+
+// TestQueueRecvInSynSentRST tests recv behavior when the TCP state
+// is SYN-SENT and an RST is sent.
+func TestQueueRecvInSynSentRST(t *testing.T) {
+ dut := testbench.NewDUT(t)
+ socket, remotePort := dut.CreateBoundSocket(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
+ conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ defer conn.Close(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) {
+ 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 {
+ 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)
+ }
+
+ // Test blocking read.
+ dut.SetNonBlocking(t, socket, false)
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+ wg.Add(1)
+ var block sync.WaitGroup
+ block.Add(1)
+ go func() {
+ defer wg.Done()
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
+ defer cancel()
+
+ block.Done()
+ // 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 n != -1 {
+ t.Errorf("expected return value %d, got %d", -1, n)
+ }
+ }()
+
+ // Wait for the goroutine to be scheduled and before it
+ // blocks on endpoint send/receive.
+ block.Wait()
+ // The following sleep is used to prevent the connection
+ // from being established before we are blocked: there is
+ // still a small time window between we sending the RPC
+ // request and the system actually being blocked.
+ time.Sleep(100 * time.Millisecond)
+
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)})
+}