diff options
author | Zeling Feng <zeling@google.com> | 2021-02-25 14:58:37 -0800 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-02-25 15:01:14 -0800 |
commit | 67761345c8f2d01f41eb8bbeb0dc8f5d94b4e576 (patch) | |
tree | 445e2c35ac05ca8e493515f1e57bb975743acdfa /test/packetimpact/tests | |
parent | 38c42bbf4ad2200f0ec72e51d1cfbbd83d782a63 (diff) |
Move SetNonblocking into posix_server
- open flags can be different on different OSs, by putting SetNonblocking into
the posix_server rather than the testbench, we can always get the right value
for O_NONBLOCK
- merged the tcp_queue_{send,receive}_in_syn_sent into a single file
PiperOrigin-RevId: 359620630
Diffstat (limited to 'test/packetimpact/tests')
-rw-r--r-- | test/packetimpact/tests/BUILD | 14 | ||||
-rw-r--r-- | test/packetimpact/tests/tcp_queue_receive_in_syn_sent_test.go | 130 | ||||
-rw-r--r-- | test/packetimpact/tests/tcp_queue_send_in_syn_sent_test.go | 131 | ||||
-rw-r--r-- | test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go | 287 |
4 files changed, 289 insertions, 273 deletions
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)}) +} |