diff options
Diffstat (limited to 'test/packetimpact/tests')
31 files changed, 0 insertions, 3900 deletions
diff --git a/test/packetimpact/tests/BUILD b/test/packetimpact/tests/BUILD deleted file mode 100644 index f850dfcd8..000000000 --- a/test/packetimpact/tests/BUILD +++ /dev/null @@ -1,332 +0,0 @@ -load("//test/packetimpact/runner:defs.bzl", "packetimpact_go_test") - -package( - default_visibility = ["//test/packetimpact:__subpackages__"], - licenses = ["notice"], -) - -packetimpact_go_test( - name = "fin_wait2_timeout", - srcs = ["fin_wait2_timeout_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "ipv4_id_uniqueness", - srcs = ["ipv4_id_uniqueness_test.go"], - deps = [ - "//pkg/abi/linux", - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "udp_discard_mcast_source_addr", - srcs = ["udp_discard_mcast_source_addr_test.go"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "udp_recv_mcast_bcast", - srcs = ["udp_recv_mcast_bcast_test.go"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@com_github_google_go_cmp//cmp:go_default_library", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "udp_any_addr_recv_unicast", - srcs = ["udp_any_addr_recv_unicast_test.go"], - deps = [ - "//pkg/tcpip", - "//test/packetimpact/testbench", - "@com_github_google_go_cmp//cmp:go_default_library", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "udp_icmp_error_propagation", - srcs = ["udp_icmp_error_propagation_test.go"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_reordering", - srcs = ["tcp_reordering_test.go"], - # TODO(b/139368047): Fix netstack then remove the line below. - expect_netstack_failure = True, - deps = [ - "//pkg/tcpip/header", - "//pkg/tcpip/seqnum", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_window_shrink", - srcs = ["tcp_window_shrink_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_zero_window_probe", - srcs = ["tcp_zero_window_probe_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_zero_window_probe_retransmit", - srcs = ["tcp_zero_window_probe_retransmit_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_zero_window_probe_usertimeout", - srcs = ["tcp_zero_window_probe_usertimeout_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_retransmits", - srcs = ["tcp_retransmits_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_outside_the_window", - srcs = ["tcp_outside_the_window_test.go"], - deps = [ - "//pkg/tcpip/header", - "//pkg/tcpip/seqnum", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_noaccept_close_rst", - srcs = ["tcp_noaccept_close_rst_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_send_window_sizes_piggyback", - srcs = ["tcp_send_window_sizes_piggyback_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_unacc_seq_ack", - srcs = ["tcp_unacc_seq_ack_test.go"], - deps = [ - "//pkg/tcpip/header", - "//pkg/tcpip/seqnum", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_paws_mechanism", - srcs = ["tcp_paws_mechanism_test.go"], - # TODO(b/156682000): Fix netstack then remove the line below. - expect_netstack_failure = True, - deps = [ - "//pkg/tcpip/header", - "//pkg/tcpip/seqnum", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_user_timeout", - srcs = ["tcp_user_timeout_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_queue_receive_in_syn_sent", - srcs = ["tcp_queue_receive_in_syn_sent_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_synsent_reset", - srcs = ["tcp_synsent_reset_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_synrcvd_reset", - srcs = ["tcp_synrcvd_reset_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_network_unreachable", - srcs = ["tcp_network_unreachable_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_cork_mss", - srcs = ["tcp_cork_mss_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_handshake_window_size", - srcs = ["tcp_handshake_window_size_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_timewait_reset", - srcs = ["tcp_timewait_reset_test.go"], - # TODO(b/168523247): Fix netstack then remove the line below. - expect_netstack_failure = True, - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "icmpv6_param_problem", - srcs = ["icmpv6_param_problem_test.go"], - # TODO(b/153485026): Fix netstack then remove the line below. - expect_netstack_failure = True, - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "ipv6_unknown_options_action", - srcs = ["ipv6_unknown_options_action_test.go"], - # TODO(b/159928940): Fix netstack then remove the line below. - expect_netstack_failure = True, - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "ipv6_fragment_reassembly", - srcs = ["ipv6_fragment_reassembly_test.go"], - # TODO(b/160919104): Fix netstack then remove the line below. - expect_netstack_failure = True, - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/buffer", - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "udp_send_recv_dgram", - srcs = ["udp_send_recv_dgram_test.go"], - deps = [ - "//test/packetimpact/testbench", - "@com_github_google_go_cmp//cmp:go_default_library", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -packetimpact_go_test( - name = "tcp_linger", - srcs = ["tcp_linger_test.go"], - deps = [ - "//pkg/tcpip/header", - "//test/packetimpact/testbench", - "@org_golang_x_sys//unix:go_default_library", - ], -) diff --git a/test/packetimpact/tests/fin_wait2_timeout_test.go b/test/packetimpact/tests/fin_wait2_timeout_test.go deleted file mode 100644 index a61054c2c..000000000 --- a/test/packetimpact/tests/fin_wait2_timeout_test.go +++ /dev/null @@ -1,75 +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 fin_wait2_timeout_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.RegisterFlags(flag.CommandLine) -} - -func TestFinWait2Timeout(t *testing.T) { - for _, tt := range []struct { - description string - linger2 bool - }{ - {"WithLinger2", true}, - {"WithoutLinger2", false}, - } { - t.Run(tt.description, func(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFd) - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) - defer conn.Close(t) - conn.Connect(t) - - acceptFd, _ := dut.Accept(t, listenFd) - if tt.linger2 { - tv := unix.Timeval{Sec: 1, Usec: 0} - dut.SetSockOptTimeval(t, acceptFd, unix.SOL_TCP, unix.TCP_LINGER2, &tv) - } - dut.Close(t, acceptFd) - - if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(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)}) - - time.Sleep(5 * time.Second) - conn.Drain(t) - - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}) - if tt.linger2 { - if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(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 { - t.Fatalf("expected no RST packets within ten seconds but got one: %s", got) - } - } - }) - } -} diff --git a/test/packetimpact/tests/icmpv6_param_problem_test.go b/test/packetimpact/tests/icmpv6_param_problem_test.go deleted file mode 100644 index 2d59d552d..000000000 --- a/test/packetimpact/tests/icmpv6_param_problem_test.go +++ /dev/null @@ -1,78 +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 icmpv6_param_problem_test - -import ( - "encoding/binary" - "flag" - "testing" - "time" - - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -// TestICMPv6ParamProblemTest sends a packet with a bad next header. The DUT -// should respond with an ICMPv6 Parameter Problem message. -func TestICMPv6ParamProblemTest(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - conn := testbench.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{}) - defer conn.Close(t) - ipv6 := testbench.IPv6{ - // 254 is reserved and used for experimentation and testing. This should - // cause an error. - NextHeader: testbench.Uint8(254), - } - icmpv6 := testbench.ICMPv6{ - Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest), - Payload: []byte("hello world"), - } - - toSend := (*testbench.Connection)(&conn).CreateFrame(t, testbench.Layers{&ipv6}, &icmpv6) - (*testbench.Connection)(&conn).SendFrame(t, toSend) - - // Build the expected ICMPv6 payload, which includes an index to the - // problematic byte and also the problematic packet as described in - // https://tools.ietf.org/html/rfc4443#page-12 . - ipv6Sent := toSend[1:] - expectedPayload, err := ipv6Sent.ToBytes() - if err != nil { - t.Fatalf("can't convert %s to bytes: %s", ipv6Sent, err) - } - - // The problematic field is the NextHeader. - b := make([]byte, 4) - binary.BigEndian.PutUint32(b, header.IPv6NextHeaderOffset) - expectedPayload = append(b, expectedPayload...) - expectedICMPv6 := testbench.ICMPv6{ - Type: testbench.ICMPv6Type(header.ICMPv6ParamProblem), - Payload: expectedPayload, - } - - paramProblem := testbench.Layers{ - &testbench.Ether{}, - &testbench.IPv6{}, - &expectedICMPv6, - } - timeout := time.Second - if _, err := conn.ExpectFrame(t, paramProblem, timeout); err != nil { - t.Errorf("expected %s within %s but got none: %s", paramProblem, timeout, err) - } -} diff --git a/test/packetimpact/tests/ipv4_id_uniqueness_test.go b/test/packetimpact/tests/ipv4_id_uniqueness_test.go deleted file mode 100644 index cf881418c..000000000 --- a/test/packetimpact/tests/ipv4_id_uniqueness_test.go +++ /dev/null @@ -1,122 +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 ipv4_id_uniqueness_test - -import ( - "context" - "flag" - "fmt" - "testing" - "time" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/abi/linux" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -func recvTCPSegment(t *testing.T, conn *testbench.TCPIPv4, expect *testbench.TCP, expectPayload *testbench.Payload) (uint16, error) { - layers, err := conn.ExpectData(t, expect, expectPayload, time.Second) - if err != nil { - return 0, fmt.Errorf("failed to receive TCP segment: %s", err) - } - if len(layers) < 2 { - return 0, fmt.Errorf("got packet with layers: %v, expected to have at least 2 layers (link and network)", layers) - } - ipv4, ok := layers[1].(*testbench.IPv4) - if !ok { - return 0, fmt.Errorf("got network layer: %T, expected: *IPv4", layers[1]) - } - if *ipv4.Flags&header.IPv4FlagDontFragment != 0 { - return 0, fmt.Errorf("got IPv4 DF=1, expected DF=0") - } - return *ipv4.ID, nil -} - -// RFC 6864 section 4.2 states: "The IPv4 ID of non-atomic datagrams MUST NOT -// be reused when sending a copy of an earlier non-atomic datagram." -// -// This test creates a TCP connection, uses the IP_MTU_DISCOVER socket option -// to force the DF bit to be 0, and checks that a retransmitted segment has a -// different IPv4 Identification value than the original segment. -func TestIPv4RetransmitIdentificationUniqueness(t *testing.T) { - for _, tc := range []struct { - name string - payload []byte - }{ - {"SmallPayload", []byte("sample data")}, - // 512 bytes is chosen because sending more than this in a single segment - // causes the retransmission to send less than the original amount. - {"512BytePayload", testbench.GenerateRandomPayload(t, 512)}, - } { - t.Run(tc.name, func(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFD) - - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) - defer conn.Close(t) - - conn.Connect(t) - remoteFD, _ := dut.Accept(t, listenFD) - defer dut.Close(t, remoteFD) - - dut.SetSockOptInt(t, remoteFD, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1) - - // TODO(b/129291778) The following socket option clears the DF bit on - // IP packets sent over the socket, and is currently not supported by - // gVisor. gVisor by default sends packets with DF=0 anyway, so the - // socket option being not supported does not affect the operation of - // this test. Once the socket option is supported, the following call - // can be changed to simply assert success. - ret, errno := dut.SetSockOptIntWithErrno(context.Background(), t, remoteFD, unix.IPPROTO_IP, linux.IP_MTU_DISCOVER, linux.IP_PMTUDISC_DONT) - if ret == -1 && errno != unix.ENOTSUP { - t.Fatalf("failed to set IP_MTU_DISCOVER socket option to IP_PMTUDISC_DONT: %s", errno) - } - - samplePayload := &testbench.Payload{Bytes: tc.payload} - - dut.Send(t, remoteFD, tc.payload, 0) - if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil { - t.Fatalf("failed to receive TCP segment sent for RTT calculation: %s", err) - } - // 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)}) - - dut.Send(t, remoteFD, tc.payload, 0) - expectTCP := &testbench.TCP{SeqNum: testbench.Uint32(uint32(*conn.RemoteSeqNum(t)))} - originalID, err := recvTCPSegment(t, &conn, expectTCP, samplePayload) - if err != nil { - t.Fatalf("failed to receive TCP segment: %s", err) - } - - retransmitID, err := recvTCPSegment(t, &conn, expectTCP, samplePayload) - if err != nil { - t.Fatalf("failed to receive retransmitted TCP segment: %s", err) - } - if originalID == retransmitID { - t.Fatalf("unexpectedly got retransmitted TCP segment with same IPv4 ID field=%d", originalID) - } - }) - } -} diff --git a/test/packetimpact/tests/ipv6_fragment_reassembly_test.go b/test/packetimpact/tests/ipv6_fragment_reassembly_test.go deleted file mode 100644 index a24c85566..000000000 --- a/test/packetimpact/tests/ipv6_fragment_reassembly_test.go +++ /dev/null @@ -1,168 +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 ipv6_fragment_reassembly_test - -import ( - "bytes" - "encoding/binary" - "encoding/hex" - "flag" - "net" - "testing" - "time" - - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/buffer" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -const ( - // The payload length for the first fragment we send. This number - // is a multiple of 8 near 750 (half of 1500). - firstPayloadLength = 752 - // The ID field for our outgoing fragments. - fragmentID = 1 - // A node must be able to accept a fragmented packet that, - // after reassembly, is as large as 1500 octets. - reassemblyCap = 1500 -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -func TestIPv6FragmentReassembly(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - conn := testbench.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{}) - defer conn.Close(t) - - firstPayloadToSend := make([]byte, firstPayloadLength) - for i := range firstPayloadToSend { - firstPayloadToSend[i] = 'A' - } - - secondPayloadLength := reassemblyCap - firstPayloadLength - header.ICMPv6EchoMinimumSize - secondPayloadToSend := firstPayloadToSend[:secondPayloadLength] - - icmpv6EchoPayload := make([]byte, 4) - binary.BigEndian.PutUint16(icmpv6EchoPayload[0:], 0) - binary.BigEndian.PutUint16(icmpv6EchoPayload[2:], 0) - icmpv6EchoPayload = append(icmpv6EchoPayload, firstPayloadToSend...) - - lIP := tcpip.Address(net.ParseIP(testbench.LocalIPv6).To16()) - rIP := tcpip.Address(net.ParseIP(testbench.RemoteIPv6).To16()) - icmpv6 := testbench.ICMPv6{ - Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest), - Code: testbench.ICMPv6Code(header.ICMPv6UnusedCode), - Payload: icmpv6EchoPayload, - } - icmpv6Bytes, err := icmpv6.ToBytes() - if err != nil { - t.Fatalf("failed to serialize ICMPv6: %s", err) - } - cksum := header.ICMPv6Checksum( - header.ICMPv6(icmpv6Bytes), - lIP, - rIP, - buffer.NewVectorisedView(len(secondPayloadToSend), []buffer.View{secondPayloadToSend}), - ) - - conn.Send(t, testbench.IPv6{}, - &testbench.IPv6FragmentExtHdr{ - FragmentOffset: testbench.Uint16(0), - MoreFragments: testbench.Bool(true), - Identification: testbench.Uint32(fragmentID), - }, - &testbench.ICMPv6{ - Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest), - Code: testbench.ICMPv6Code(header.ICMPv6UnusedCode), - Payload: icmpv6EchoPayload, - Checksum: &cksum, - }) - - icmpv6ProtoNum := header.IPv6ExtensionHeaderIdentifier(header.ICMPv6ProtocolNumber) - - conn.Send(t, testbench.IPv6{}, - &testbench.IPv6FragmentExtHdr{ - NextHeader: &icmpv6ProtoNum, - FragmentOffset: testbench.Uint16((firstPayloadLength + header.ICMPv6EchoMinimumSize) / 8), - MoreFragments: testbench.Bool(false), - Identification: testbench.Uint32(fragmentID), - }, - &testbench.Payload{ - Bytes: secondPayloadToSend, - }) - - gotEchoReplyFirstPart, err := conn.ExpectFrame(t, testbench.Layers{ - &testbench.Ether{}, - &testbench.IPv6{}, - &testbench.IPv6FragmentExtHdr{ - FragmentOffset: testbench.Uint16(0), - MoreFragments: testbench.Bool(true), - }, - &testbench.ICMPv6{ - Type: testbench.ICMPv6Type(header.ICMPv6EchoReply), - Code: testbench.ICMPv6Code(header.ICMPv6UnusedCode), - }, - }, time.Second) - if err != nil { - t.Fatalf("expected a fragmented ICMPv6 Echo Reply, but got none: %s", err) - } - - id := *gotEchoReplyFirstPart[2].(*testbench.IPv6FragmentExtHdr).Identification - gotFirstPayload, err := gotEchoReplyFirstPart[len(gotEchoReplyFirstPart)-1].ToBytes() - if err != nil { - t.Fatalf("failed to serialize ICMPv6: %s", err) - } - icmpPayload := gotFirstPayload[header.ICMPv6EchoMinimumSize:] - receivedLen := len(icmpPayload) - wantSecondPayloadLen := reassemblyCap - header.ICMPv6EchoMinimumSize - receivedLen - wantFirstPayload := make([]byte, receivedLen) - for i := range wantFirstPayload { - wantFirstPayload[i] = 'A' - } - wantSecondPayload := wantFirstPayload[:wantSecondPayloadLen] - if !bytes.Equal(icmpPayload, wantFirstPayload) { - t.Fatalf("received unexpected payload, got: %s, want: %s", - hex.Dump(icmpPayload), - hex.Dump(wantFirstPayload)) - } - - gotEchoReplySecondPart, err := conn.ExpectFrame(t, testbench.Layers{ - &testbench.Ether{}, - &testbench.IPv6{}, - &testbench.IPv6FragmentExtHdr{ - NextHeader: &icmpv6ProtoNum, - FragmentOffset: testbench.Uint16(uint16((receivedLen + header.ICMPv6EchoMinimumSize) / 8)), - MoreFragments: testbench.Bool(false), - Identification: &id, - }, - &testbench.ICMPv6{}, - }, time.Second) - if err != nil { - t.Fatalf("expected the rest of ICMPv6 Echo Reply, but got none: %s", err) - } - secondPayload, err := gotEchoReplySecondPart[len(gotEchoReplySecondPart)-1].ToBytes() - if err != nil { - t.Fatalf("failed to serialize ICMPv6 Echo Reply: %s", err) - } - if !bytes.Equal(secondPayload, wantSecondPayload) { - t.Fatalf("received unexpected payload, got: %s, want: %s", - hex.Dump(secondPayload), - hex.Dump(wantSecondPayload)) - } -} diff --git a/test/packetimpact/tests/ipv6_unknown_options_action_test.go b/test/packetimpact/tests/ipv6_unknown_options_action_test.go deleted file mode 100644 index e79d74476..000000000 --- a/test/packetimpact/tests/ipv6_unknown_options_action_test.go +++ /dev/null @@ -1,187 +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 ipv6_unknown_options_action_test - -import ( - "encoding/binary" - "flag" - "net" - "testing" - "time" - - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -func mkHopByHopOptionsExtHdr(optType byte) testbench.Layer { - return &testbench.IPv6HopByHopOptionsExtHdr{ - Options: []byte{optType, 0x04, 0x00, 0x00, 0x00, 0x00}, - } -} - -func mkDestinationOptionsExtHdr(optType byte) testbench.Layer { - return &testbench.IPv6DestinationOptionsExtHdr{ - Options: []byte{optType, 0x04, 0x00, 0x00, 0x00, 0x00}, - } -} - -func optionTypeFromAction(action header.IPv6OptionUnknownAction) byte { - return byte(action << 6) -} - -func TestIPv6UnknownOptionAction(t *testing.T) { - for _, tt := range []struct { - description string - mkExtHdr func(optType byte) testbench.Layer - action header.IPv6OptionUnknownAction - multicastDst bool - wantICMPv6 bool - }{ - { - description: "0b00/hbh", - mkExtHdr: mkHopByHopOptionsExtHdr, - action: header.IPv6OptionUnknownActionSkip, - multicastDst: false, - wantICMPv6: false, - }, - { - description: "0b01/hbh", - mkExtHdr: mkHopByHopOptionsExtHdr, - action: header.IPv6OptionUnknownActionDiscard, - multicastDst: false, - wantICMPv6: false, - }, - { - description: "0b10/hbh/unicast", - mkExtHdr: mkHopByHopOptionsExtHdr, - action: header.IPv6OptionUnknownActionDiscardSendICMP, - multicastDst: false, - wantICMPv6: true, - }, - { - description: "0b10/hbh/multicast", - mkExtHdr: mkHopByHopOptionsExtHdr, - action: header.IPv6OptionUnknownActionDiscardSendICMP, - multicastDst: true, - wantICMPv6: true, - }, - { - description: "0b11/hbh/unicast", - mkExtHdr: mkHopByHopOptionsExtHdr, - action: header.IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest, - multicastDst: false, - wantICMPv6: true, - }, - { - description: "0b11/hbh/multicast", - mkExtHdr: mkHopByHopOptionsExtHdr, - action: header.IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest, - multicastDst: true, - wantICMPv6: false, - }, - { - description: "0b00/destination", - mkExtHdr: mkDestinationOptionsExtHdr, - action: header.IPv6OptionUnknownActionSkip, - multicastDst: false, - wantICMPv6: false, - }, - { - description: "0b01/destination", - mkExtHdr: mkDestinationOptionsExtHdr, - action: header.IPv6OptionUnknownActionDiscard, - multicastDst: false, - wantICMPv6: false, - }, - { - description: "0b10/destination/unicast", - mkExtHdr: mkDestinationOptionsExtHdr, - action: header.IPv6OptionUnknownActionDiscardSendICMP, - multicastDst: false, - wantICMPv6: true, - }, - { - description: "0b10/destination/multicast", - mkExtHdr: mkDestinationOptionsExtHdr, - action: header.IPv6OptionUnknownActionDiscardSendICMP, - multicastDst: true, - wantICMPv6: true, - }, - { - description: "0b11/destination/unicast", - mkExtHdr: mkDestinationOptionsExtHdr, - action: header.IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest, - multicastDst: false, - wantICMPv6: true, - }, - { - description: "0b11/destination/multicast", - mkExtHdr: mkDestinationOptionsExtHdr, - action: header.IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest, - multicastDst: true, - wantICMPv6: false, - }, - } { - t.Run(tt.description, func(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - ipv6Conn := testbench.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{}) - conn := (*testbench.Connection)(&ipv6Conn) - defer ipv6Conn.Close(t) - - outgoingOverride := testbench.Layers{} - if tt.multicastDst { - outgoingOverride = testbench.Layers{&testbench.IPv6{ - DstAddr: testbench.Address(tcpip.Address(net.ParseIP("ff02::1"))), - }} - } - - outgoing := conn.CreateFrame(t, outgoingOverride, tt.mkExtHdr(optionTypeFromAction(tt.action))) - conn.SendFrame(t, outgoing) - ipv6Sent := outgoing[1:] - invokingPacket, err := ipv6Sent.ToBytes() - if err != nil { - t.Fatalf("failed to serialize the outgoing packet: %s", err) - } - icmpv6Payload := make([]byte, 4) - // The pointer in the ICMPv6 parameter problem message should point to - // the option type of the unknown option. In our test case, it is the - // first option in the extension header whose option type is 2 bytes - // after the IPv6 header (after NextHeader and ExtHdrLen). - binary.BigEndian.PutUint32(icmpv6Payload, header.IPv6MinimumSize+2) - icmpv6Payload = append(icmpv6Payload, invokingPacket...) - gotICMPv6, err := ipv6Conn.ExpectFrame(t, testbench.Layers{ - &testbench.Ether{}, - &testbench.IPv6{}, - &testbench.ICMPv6{ - Type: testbench.ICMPv6Type(header.ICMPv6ParamProblem), - Code: testbench.ICMPv6Code(header.ICMPv6UnknownOption), - Payload: icmpv6Payload, - }, - }, time.Second) - if tt.wantICMPv6 && err != nil { - t.Fatalf("expected ICMPv6 Parameter Problem but got none: %s", err) - } - if !tt.wantICMPv6 && gotICMPv6 != nil { - t.Fatalf("expected no ICMPv6 Parameter Problem but got one: %s", gotICMPv6) - } - }) - } -} diff --git a/test/packetimpact/tests/tcp_cork_mss_test.go b/test/packetimpact/tests/tcp_cork_mss_test.go deleted file mode 100644 index 8feea4a82..000000000 --- a/test/packetimpact/tests/tcp_cork_mss_test.go +++ /dev/null @@ -1,84 +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_cork_mss_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.RegisterFlags(flag.CommandLine) -} - -// TestTCPCorkMSS tests for segment coalesce and split as per MSS. -func TestTCPCorkMSS(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFD) - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) - defer conn.Close(t) - - const mss = uint32(header.TCPDefaultMSS) - options := make([]byte, header.TCPOptionMSSLength) - header.EncodeMSSOption(mss, options) - conn.ConnectWithOptions(t, options) - - acceptFD, _ := dut.Accept(t, listenFD) - defer dut.Close(t, acceptFD) - - dut.SetSockOptInt(t, acceptFD, unix.IPPROTO_TCP, unix.TCP_CORK, 1) - - // Let the dut application send 2 small segments to be held up and coalesced - // until the application sends a larger segment to fill up to > MSS. - sampleData := []byte("Sample Data") - dut.Send(t, acceptFD, sampleData, 0) - dut.Send(t, acceptFD, sampleData, 0) - - expectedData := sampleData - expectedData = append(expectedData, sampleData...) - largeData := make([]byte, mss+1) - expectedData = append(expectedData, largeData...) - dut.Send(t, acceptFD, largeData, 0) - - // 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 { - t.Fatalf("expected payload was not received: %s", err) - } - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(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 { - 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)))}) - 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 { - t.Fatalf("expected payload was not received: %s", err) - } - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}) -} diff --git a/test/packetimpact/tests/tcp_handshake_window_size_test.go b/test/packetimpact/tests/tcp_handshake_window_size_test.go deleted file mode 100644 index 22937d92f..000000000 --- a/test/packetimpact/tests/tcp_handshake_window_size_test.go +++ /dev/null @@ -1,66 +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_handshake_window_size_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.RegisterFlags(flag.CommandLine) -} - -// TestTCPHandshakeWindowSize tests if the stack is honoring the window size -// communicated during handshake. -func TestTCPHandshakeWindowSize(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFD) - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) - 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 { - t.Fatalf("expected SYN-ACK: %s", err) - } - // Update the advertised window size to a non-zero value with the ACK that - // completes the handshake. - // - // 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))}) - - acceptFd, _ := dut.Accept(t, listenFD) - defer dut.Close(t, acceptFd) - - sampleData := []byte("Sample Data") - samplePayload := &testbench.Payload{Bytes: sampleData} - - // Since we advertised a zero window followed by a non-zero window, - // 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 { - t.Fatalf("expected payload was not received: %s", err) - } -} diff --git a/test/packetimpact/tests/tcp_linger_test.go b/test/packetimpact/tests/tcp_linger_test.go deleted file mode 100644 index 913e49e06..000000000 --- a/test/packetimpact/tests/tcp_linger_test.go +++ /dev/null @@ -1,253 +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_linger_test - -import ( - "context" - "flag" - "syscall" - "testing" - "time" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -func createSocket(t *testing.T, dut testbench.DUT) (int32, int32, testbench.TCPIPv4) { - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) - conn.Connect(t) - acceptFD, _ := dut.Accept(t, listenFD) - return acceptFD, listenFD, conn -} - -func closeAll(t *testing.T, dut testbench.DUT, listenFD int32, conn testbench.TCPIPv4) { - conn.Close(t) - dut.Close(t, listenFD) - dut.TearDown() -} - -// lingerDuration is the timeout value used with SO_LINGER socket option. -const lingerDuration = 3 * time.Second - -// TestTCPLingerZeroTimeout tests when SO_LINGER is set with zero timeout. DUT -// should send RST-ACK when socket is closed. -func TestTCPLingerZeroTimeout(t *testing.T) { - // Create a socket, listen, TCP connect, and accept. - dut := testbench.NewDUT(t) - acceptFD, listenFD, conn := createSocket(t, dut) - defer closeAll(t, dut, listenFD, conn) - - dut.SetSockLingerOption(t, acceptFD, 0, true) - 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 { - t.Errorf("expected RST-ACK packet within a second but got none: %s", err) - } - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}) -} - -// TestTCPLingerOff tests when SO_LINGER is not set. DUT should send FIN-ACK -// when socket is closed. -func TestTCPLingerOff(t *testing.T) { - // Create a socket, listen, TCP connect, and accept. - dut := testbench.NewDUT(t) - acceptFD, listenFD, conn := createSocket(t, dut) - defer closeAll(t, dut, listenFD, conn) - - 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 { - t.Errorf("expected FIN-ACK packet within a second but got none: %s", err) - } - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}) -} - -// TestTCPLingerNonZeroTimeout tests when SO_LINGER is set with non-zero timeout. -// DUT should close the socket after timeout. -func TestTCPLingerNonZeroTimeout(t *testing.T) { - for _, tt := range []struct { - description string - lingerOn bool - }{ - {"WithNonZeroLinger", true}, - {"WithoutLinger", false}, - } { - t.Run(tt.description, func(t *testing.T) { - // Create a socket, listen, TCP connect, and accept. - dut := testbench.NewDUT(t) - acceptFD, listenFD, conn := createSocket(t, dut) - defer closeAll(t, dut, listenFD, conn) - - dut.SetSockLingerOption(t, acceptFD, lingerDuration, tt.lingerOn) - - // Increase timeout as Close will take longer time to - // return when SO_LINGER is set with non-zero timeout. - timeout := lingerDuration + 1*time.Second - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - start := time.Now() - dut.CloseWithErrno(ctx, t, acceptFD) - end := time.Now() - diff := end.Sub(start) - - if tt.lingerOn && diff < lingerDuration { - t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration) - } else if !tt.lingerOn && diff > 1*time.Second { - 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 { - t.Errorf("expected FIN-ACK packet within a second but got none: %s", err) - } - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}) - }) - } -} - -// TestTCPLingerSendNonZeroTimeout tests when SO_LINGER is set with non-zero -// timeout and send a packet. DUT should close the socket after timeout. -func TestTCPLingerSendNonZeroTimeout(t *testing.T) { - for _, tt := range []struct { - description string - lingerOn bool - }{ - {"WithSendNonZeroLinger", true}, - {"WithoutLinger", false}, - } { - t.Run(tt.description, func(t *testing.T) { - // Create a socket, listen, TCP connect, and accept. - dut := testbench.NewDUT(t) - acceptFD, listenFD, conn := createSocket(t, dut) - defer closeAll(t, dut, listenFD, conn) - - dut.SetSockLingerOption(t, acceptFD, lingerDuration, tt.lingerOn) - - // Send data. - sampleData := []byte("Sample Data") - dut.Send(t, acceptFD, sampleData, 0) - - // Increase timeout as Close will take longer time to - // return when SO_LINGER is set with non-zero timeout. - timeout := lingerDuration + 1*time.Second - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - start := time.Now() - dut.CloseWithErrno(ctx, t, acceptFD) - end := time.Now() - diff := end.Sub(start) - - if tt.lingerOn && diff < lingerDuration { - t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration) - } else if !tt.lingerOn && diff > 1*time.Second { - t.Errorf("expected close to return within a second, but returned later") - } - - samplePayload := &testbench.Payload{Bytes: sampleData} - if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil { - 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 { - t.Errorf("expected FIN-ACK packet within a second but got none: %s", err) - } - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}) - }) - } -} - -// TestTCPLingerShutdownZeroTimeout tests SO_LINGER with shutdown() and zero -// timeout. DUT should send RST-ACK when socket is closed. -func TestTCPLingerShutdownZeroTimeout(t *testing.T) { - // Create a socket, listen, TCP connect, and accept. - dut := testbench.NewDUT(t) - acceptFD, listenFD, conn := createSocket(t, dut) - defer closeAll(t, dut, listenFD, conn) - - dut.SetSockLingerOption(t, acceptFD, 0, true) - dut.Shutdown(t, acceptFD, syscall.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 { - 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 { - t.Errorf("expected RST-ACK packet within a second but got none: %s", err) - } - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}) -} - -// TestTCPLingerShutdownSendNonZeroTimeout tests SO_LINGER with shutdown() and -// non-zero timeout. DUT should close the socket after timeout. -func TestTCPLingerShutdownSendNonZeroTimeout(t *testing.T) { - for _, tt := range []struct { - description string - lingerOn bool - }{ - {"shutdownRDWR", true}, - {"shutdownRDWR", false}, - } { - t.Run(tt.description, func(t *testing.T) { - // Create a socket, listen, TCP connect, and accept. - dut := testbench.NewDUT(t) - acceptFD, listenFD, conn := createSocket(t, dut) - defer closeAll(t, dut, listenFD, conn) - - dut.SetSockLingerOption(t, acceptFD, lingerDuration, tt.lingerOn) - - // Send data. - sampleData := []byte("Sample Data") - dut.Send(t, acceptFD, sampleData, 0) - - dut.Shutdown(t, acceptFD, syscall.SHUT_RDWR) - - // Increase timeout as Close will take longer time to - // return when SO_LINGER is set with non-zero timeout. - timeout := lingerDuration + 1*time.Second - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - start := time.Now() - dut.CloseWithErrno(ctx, t, acceptFD) - end := time.Now() - diff := end.Sub(start) - - if tt.lingerOn && diff < lingerDuration { - t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration) - } else if !tt.lingerOn && diff > 1*time.Second { - t.Errorf("expected close to return within a second, but returned later") - } - - samplePayload := &testbench.Payload{Bytes: sampleData} - if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil { - 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 { - t.Errorf("expected FIN-ACK packet within a second but got none: %s", err) - } - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}) - }) - } -} diff --git a/test/packetimpact/tests/tcp_network_unreachable_test.go b/test/packetimpact/tests/tcp_network_unreachable_test.go deleted file mode 100644 index 2f57dff19..000000000 --- a/test/packetimpact/tests/tcp_network_unreachable_test.go +++ /dev/null @@ -1,141 +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_synsent_reset_test - -import ( - "context" - "flag" - "net" - "syscall" - "testing" - "time" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -// TestTCPSynSentUnreachable verifies that TCP connections fail immediately when -// an ICMP destination unreachable message is sent in response to the inital -// SYN. -func TestTCPSynSentUnreachable(t *testing.T) { - // Create the DUT and connection. - dut := testbench.NewDUT(t) - defer dut.TearDown() - clientFD, clientPort := dut.CreateBoundSocket(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, net.ParseIP(testbench.RemoteIPv4)) - port := uint16(9001) - conn := testbench.NewTCPIPv4(t, testbench.TCP{SrcPort: &port, DstPort: &clientPort}, testbench.TCP{SrcPort: &clientPort, DstPort: &port}) - defer conn.Close(t) - - // Bring the DUT to SYN-SENT state with a non-blocking connect. - ctx, cancel := context.WithTimeout(context.Background(), testbench.RPCTimeout) - defer cancel() - sa := unix.SockaddrInet4{Port: int(port)} - copy(sa.Addr[:], net.IP(net.ParseIP(testbench.LocalIPv4)).To4()) - if _, err := dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != syscall.Errno(unix.EINPROGRESS) { - t.Errorf("expected connect to fail with EINPROGRESS, but got %v", err) - } - - // Get the SYN. - tcpLayers, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)}, nil, time.Second) - if err != nil { - t.Fatalf("expected SYN: %s", err) - } - - // Send a host unreachable message. - rawConn := (*testbench.Connection)(&conn) - layers := rawConn.CreateFrame(t, nil) - layers = layers[:len(layers)-1] - const ipLayer = 1 - const tcpLayer = ipLayer + 1 - ip, ok := tcpLayers[ipLayer].(*testbench.IPv4) - if !ok { - t.Fatalf("expected %s to be IPv4", tcpLayers[ipLayer]) - } - tcp, ok := tcpLayers[tcpLayer].(*testbench.TCP) - if !ok { - t.Fatalf("expected %s to be TCP", tcpLayers[tcpLayer]) - } - var icmpv4 testbench.ICMPv4 = testbench.ICMPv4{ - Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable), - Code: testbench.ICMPv4Code(header.ICMPv4HostUnreachable)} - layers = append(layers, &icmpv4, ip, tcp) - rawConn.SendFrameStateless(t, layers) - - if _, err = dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != syscall.Errno(unix.EHOSTUNREACH) { - t.Errorf("expected connect to fail with EHOSTUNREACH, but got %v", err) - } -} - -// TestTCPSynSentUnreachable6 verifies that TCP connections fail immediately when -// an ICMP destination unreachable message is sent in response to the inital -// SYN. -func TestTCPSynSentUnreachable6(t *testing.T) { - // Create the DUT and connection. - dut := testbench.NewDUT(t) - defer dut.TearDown() - clientFD, clientPort := dut.CreateBoundSocket(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, net.ParseIP(testbench.RemoteIPv6)) - conn := testbench.NewTCPIPv6(t, testbench.TCP{DstPort: &clientPort}, testbench.TCP{SrcPort: &clientPort}) - defer conn.Close(t) - - // Bring the DUT to SYN-SENT state with a non-blocking connect. - ctx, cancel := context.WithTimeout(context.Background(), testbench.RPCTimeout) - defer cancel() - sa := unix.SockaddrInet6{ - Port: int(conn.SrcPort()), - ZoneId: uint32(testbench.RemoteInterfaceID), - } - copy(sa.Addr[:], net.IP(net.ParseIP(testbench.LocalIPv6)).To16()) - if _, err := dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != syscall.Errno(unix.EINPROGRESS) { - t.Errorf("expected connect to fail with EINPROGRESS, but got %v", err) - } - - // Get the SYN. - tcpLayers, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)}, nil, time.Second) - if err != nil { - t.Fatalf("expected SYN: %s", err) - } - - // Send a host unreachable message. - rawConn := (*testbench.Connection)(&conn) - layers := rawConn.CreateFrame(t, nil) - layers = layers[:len(layers)-1] - const ipLayer = 1 - const tcpLayer = ipLayer + 1 - ip, ok := tcpLayers[ipLayer].(*testbench.IPv6) - if !ok { - t.Fatalf("expected %s to be IPv6", tcpLayers[ipLayer]) - } - tcp, ok := tcpLayers[tcpLayer].(*testbench.TCP) - if !ok { - t.Fatalf("expected %s to be TCP", tcpLayers[tcpLayer]) - } - var icmpv6 testbench.ICMPv6 = testbench.ICMPv6{ - Type: testbench.ICMPv6Type(header.ICMPv6DstUnreachable), - Code: testbench.ICMPv6Code(header.ICMPv6NetworkUnreachable), - // Per RFC 4443 3.1, the payload contains 4 zeroed bytes. - Payload: []byte{0, 0, 0, 0}, - } - layers = append(layers, &icmpv6, ip, tcp) - rawConn.SendFrameStateless(t, layers) - - if _, err = dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != syscall.Errno(unix.ENETUNREACH) { - t.Errorf("expected connect to fail with ENETUNREACH, but got %v", err) - } -} diff --git a/test/packetimpact/tests/tcp_noaccept_close_rst_test.go b/test/packetimpact/tests/tcp_noaccept_close_rst_test.go deleted file mode 100644 index 82b7a85ff..000000000 --- a/test/packetimpact/tests/tcp_noaccept_close_rst_test.go +++ /dev/null @@ -1,42 +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_noaccept_close_rst_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.RegisterFlags(flag.CommandLine) -} - -func TestTcpNoAcceptCloseReset(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) - conn.Connect(t) - defer conn.Close(t) - dut.Close(t, listenFd) - if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(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_test.go b/test/packetimpact/tests/tcp_outside_the_window_test.go deleted file mode 100644 index 08f759f7c..000000000 --- a/test/packetimpact/tests/tcp_outside_the_window_test.go +++ /dev/null @@ -1,93 +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_outside_the_window_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.RegisterFlags(flag.CommandLine) -} - -// TestTCPOutsideTheWindows tests the behavior of the DUT when packets arrive -// that are inside or outside the TCP window. Packets that are outside the -// window should force an extra ACK, as described in RFC793 page 69: -// https://tools.ietf.org/html/rfc793#page-69 -func TestTCPOutsideTheWindow(t *testing.T) { - for _, tt := range []struct { - description string - tcpFlags uint8 - payload []testbench.Layer - seqNumOffset seqnum.Size - expectACK bool - }{ - {"SYN", header.TCPFlagSyn, nil, 0, true}, - {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 0, true}, - {"ACK", header.TCPFlagAck, nil, 0, false}, - {"FIN", header.TCPFlagFin, nil, 0, false}, - {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("abc123")}}, 0, true}, - - {"SYN", header.TCPFlagSyn, nil, 1, true}, - {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 1, true}, - {"ACK", header.TCPFlagAck, nil, 1, true}, - {"FIN", header.TCPFlagFin, nil, 1, false}, - {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("abc123")}}, 1, true}, - - {"SYN", header.TCPFlagSyn, nil, 2, true}, - {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 2, true}, - {"ACK", header.TCPFlagAck, nil, 2, true}, - {"FIN", header.TCPFlagFin, nil, 2, false}, - {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("abc123")}}, 2, true}, - } { - t.Run(fmt.Sprintf("%s%d", tt.description, tt.seqNumOffset), func(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFD) - conn := testbench.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) - - windowSize := seqnum.Size(*conn.SynAck(t).WindowSize) + tt.seqNumOffset - conn.Drain(t) - // Ignore whatever incrementing that this out-of-order packet might cause - // to the AckNum. - localSeqNum := testbench.Uint32(uint32(*conn.LocalSeqNum(t))) - conn.Send(t, testbench.TCP{ - Flags: testbench.Uint8(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) - if tt.expectACK && err != nil { - t.Fatalf("expected an ACK packet within %s but got none: %s", timeout, err) - } - if !tt.expectACK && gotACK != nil { - t.Fatalf("expected no ACK packet within %s but got one: %s", timeout, gotACK) - } - }) - } -} diff --git a/test/packetimpact/tests/tcp_paws_mechanism_test.go b/test/packetimpact/tests/tcp_paws_mechanism_test.go deleted file mode 100644 index 37f3b56dd..000000000 --- a/test/packetimpact/tests/tcp_paws_mechanism_test.go +++ /dev/null @@ -1,109 +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_paws_mechanism_test - -import ( - "encoding/hex" - "flag" - "testing" - "time" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -func TestPAWSMechanism(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFD) - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) - defer conn.Close(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) - if err != nil { - t.Fatalf("didn't get synack during handshake: %s", err) - } - parsedSynOpts := header.ParseSynOptions(synAck.Options, true) - if !parsedSynOpts.TS { - t.Fatalf("expected TSOpt from DUT, options we got:\n%s", hex.Dump(synAck.Options)) - } - tsecr := parsedSynOpts.TSVal - header.EncodeTSOption(currentTS(), tsecr, options) - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), Options: options}) - acceptFD, _ := dut.Accept(t, listenFD) - defer dut.Close(t, acceptFD) - - sampleData := []byte("Sample Data") - sentTSVal := currentTS() - header.EncodeTSOption(sentTSVal, tsecr, options) - // 3ms here is chosen arbitrarily to make sure we have increasing timestamps - // 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}) - - gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second) - if err != nil { - t.Fatalf("expected an ACK but got none: %s", err) - } - - parsedOpts := header.ParseTCPOptions(gotTCP.Options) - if !parsedOpts.TS { - t.Fatalf("expected TS option in response, options we got:\n%s", hex.Dump(gotTCP.Options)) - } - if parsedOpts.TSVal < tsecr { - t.Fatalf("TSVal should be non-decreasing, but %d < %d", parsedOpts.TSVal, tsecr) - } - if parsedOpts.TSEcr != sentTSVal { - t.Fatalf("TSEcr should match our sent TSVal, %d != %d", parsedOpts.TSEcr, sentTSVal) - } - tsecr = parsedOpts.TSVal - lastAckNum := gotTCP.AckNum - - badTSVal := sentTSVal - 100 - header.EncodeTSOption(badTSVal, tsecr, options) - // 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}) - - gotTCP, err = conn.Expect(t, testbench.TCP{AckNum: lastAckNum, Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second) - if err != nil { - t.Fatalf("expected segment with AckNum %d but got none: %s", lastAckNum, err) - } - parsedOpts = header.ParseTCPOptions(gotTCP.Options) - if !parsedOpts.TS { - t.Fatalf("expected TS option in response, options we got:\n%s", hex.Dump(gotTCP.Options)) - } - if parsedOpts.TSVal < tsecr { - t.Fatalf("TSVal should be non-decreasing, but %d < %d", parsedOpts.TSVal, tsecr) - } - if parsedOpts.TSEcr != sentTSVal { - t.Fatalf("TSEcr should match our sent TSVal, %d != %d", parsedOpts.TSEcr, sentTSVal) - } -} - -func currentTS() uint32 { - return uint32(time.Now().UnixNano() / 1e6) -} 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 d9f3ea0f2..000000000 --- a/test/packetimpact/tests/tcp_queue_receive_in_syn_sent_test.go +++ /dev/null @@ -1,132 +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" - "net" - "sync" - "syscall" - "testing" - "time" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(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) - defer dut.TearDown() - - socket, remotePort := dut.CreateBoundSocket(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, net.ParseIP(testbench.RemoteIPv4)) - conn := testbench.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_reordering_test.go b/test/packetimpact/tests/tcp_reordering_test.go deleted file mode 100644 index b4aeaab57..000000000 --- a/test/packetimpact/tests/tcp_reordering_test.go +++ /dev/null @@ -1,174 +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 reordering_test - -import ( - "flag" - "testing" - "time" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/pkg/tcpip/seqnum" - tb "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - tb.RegisterFlags(flag.CommandLine) -} - -func TestReorderingWindow(t *testing.T) { - dut := tb.NewDUT(t) - defer dut.TearDown() - listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFd) - conn := tb.NewTCPIPv4(t, tb.TCP{DstPort: &remotePort}, tb.TCP{SrcPort: &remotePort}) - defer conn.Close(t) - - // Enable SACK. - opts := make([]byte, 40) - optsOff := 0 - optsOff += header.EncodeNOP(opts[optsOff:]) - optsOff += header.EncodeNOP(opts[optsOff:]) - optsOff += header.EncodeSACKPermittedOption(opts[optsOff:]) - - // Ethernet guarantees that the MTU is at least 1500 bytes. - const minMTU = 1500 - const mss = minMTU - header.IPv4MinimumSize - header.TCPMinimumSize - optsOff += header.EncodeMSSOption(mss, opts[optsOff:]) - - conn.ConnectWithOptions(t, opts[:optsOff]) - - acceptFd, _ := dut.Accept(t, listenFd) - defer dut.Close(t, acceptFd) - - if tb.Native { - // Linux has changed its handling of reordering, force the old behavior. - dut.SetSockOpt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_CONGESTION, []byte("reno")) - } - - pls := dut.GetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_MAXSEG) - if !tb.Native { - // netstack does not impliment TCP_MAXSEG correctly. Fake it - // here. Netstack uses the max SACK size which is 32. The MSS - // option is 8 bytes, making the total 36 bytes. - pls = mss - 36 - } - - payload := make([]byte, pls) - - seqNum1 := *conn.RemoteSeqNum(t) - const numPkts = 10 - // Send some packets, checking that we receive each. - for i, sn := 0, seqNum1; i < numPkts; i++ { - dut.Send(t, acceptFd, payload, 0) - - gotOne, err := conn.Expect(t, tb.TCP{SeqNum: tb.Uint32(uint32(sn))}, time.Second) - sn.UpdateForward(seqnum.Size(len(payload))) - if err != nil { - t.Errorf("Expect #%d: %s", i+1, err) - continue - } - if gotOne == nil { - t.Errorf("#%d: expected a packet within a second but got none", i+1) - } - } - - seqNum2 := *conn.RemoteSeqNum(t) - - // SACK packets #2-4. - sackBlock := make([]byte, 40) - sbOff := 0 - sbOff += header.EncodeNOP(sackBlock[sbOff:]) - sbOff += header.EncodeNOP(sackBlock[sbOff:]) - sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{ - seqNum1.Add(seqnum.Size(len(payload))), - seqNum1.Add(seqnum.Size(4 * len(payload))), - }}, sackBlock[sbOff:]) - conn.Send(t, tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), AckNum: tb.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]}) - - // ACK first packet. - conn.Send(t, tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), AckNum: tb.Uint32(uint32(seqNum1) + uint32(len(payload)))}) - - // Check for retransmit. - gotOne, err := conn.Expect(t, tb.TCP{SeqNum: tb.Uint32(uint32(seqNum1))}, time.Second) - if err != nil { - t.Error("Expect for retransmit:", err) - } - if gotOne == nil { - t.Error("expected a retransmitted packet within a second but got none") - } - - // ACK all send packets with a DSACK block for packet #1. This tells - // the other end that we got both the original and retransmit for - // packet #1. - dsackBlock := make([]byte, 40) - dsbOff := 0 - dsbOff += header.EncodeNOP(dsackBlock[dsbOff:]) - dsbOff += header.EncodeNOP(dsackBlock[dsbOff:]) - dsbOff += header.EncodeSACKBlocks([]header.SACKBlock{{ - seqNum1.Add(seqnum.Size(len(payload))), - seqNum1.Add(seqnum.Size(4 * len(payload))), - }}, dsackBlock[dsbOff:]) - - conn.Send(t, tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), AckNum: tb.Uint32(uint32(seqNum2)), Options: dsackBlock[:dsbOff]}) - - // Send half of the original window of packets, checking that we - // received each. - for i, sn := 0, seqNum2; i < numPkts/2; i++ { - dut.Send(t, acceptFd, payload, 0) - - gotOne, err := conn.Expect(t, tb.TCP{SeqNum: tb.Uint32(uint32(sn))}, time.Second) - sn.UpdateForward(seqnum.Size(len(payload))) - if err != nil { - t.Errorf("Expect #%d: %s", i+1, err) - continue - } - if gotOne == nil { - t.Errorf("#%d: expected a packet within a second but got none", i+1) - } - } - - if !tb.Native { - // The window should now be halved, so we should receive any - // more, even if we send them. - dut.Send(t, acceptFd, payload, 0) - if got, err := conn.Expect(t, tb.TCP{}, 100*time.Millisecond); got != nil || err == nil { - t.Fatalf("expected no packets within 100 millisecond, but got one: %s", got) - } - return - } - - // Linux reduces the window by three. Check that we can receive the rest. - for i, sn := 0, seqNum2.Add(seqnum.Size(numPkts/2*len(payload))); i < 2; i++ { - dut.Send(t, acceptFd, payload, 0) - - gotOne, err := conn.Expect(t, tb.TCP{SeqNum: tb.Uint32(uint32(sn))}, time.Second) - sn.UpdateForward(seqnum.Size(len(payload))) - if err != nil { - t.Errorf("Expect #%d: %s", i+1, err) - continue - } - if gotOne == nil { - t.Errorf("#%d: expected a packet within a second but got none", i+1) - } - } - - // The window should now be full. - dut.Send(t, acceptFd, payload, 0) - if got, err := conn.Expect(t, tb.TCP{}, 100*time.Millisecond); got != nil || err == nil { - t.Fatalf("expected no packets within 100 millisecond, but got one: %s", got) - } -} diff --git a/test/packetimpact/tests/tcp_retransmits_test.go b/test/packetimpact/tests/tcp_retransmits_test.go deleted file mode 100644 index 072014ff8..000000000 --- a/test/packetimpact/tests/tcp_retransmits_test.go +++ /dev/null @@ -1,84 +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_retransmits_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.RegisterFlags(flag.CommandLine) -} - -// TestRetransmits tests retransmits occur at exponentially increasing -// time intervals. -func TestRetransmits(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFd) - conn := testbench.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.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1) - - sampleData := []byte("Sample Data") - samplePayload := &testbench.Payload{Bytes: sampleData} - - 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)}) - - startRTO := time.Second - current := startRTO - first := 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, startRTO); err != nil { - t.Fatalf("expected payload was not received: %s", err) - } - // Expect retransmits of the same segment. - for i := 0; i < 5; i++ { - start := time.Now() - if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: seq}, samplePayload, 2*current); err != nil { - t.Fatalf("expected payload was not received: %s loop %d", err, i) - } - if i == 0 { - startRTO = time.Now().Sub(first) - current = 2 * startRTO - continue - } - // Check if the probes came at exponentially increasing intervals. - if p := time.Since(start); p < current-startRTO { - t.Fatalf("retransmit came sooner interval %d probe %d", p, i) - } - current *= 2 - } -} diff --git a/test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go b/test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go deleted file mode 100644 index f91b06ba1..000000000 --- a/test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go +++ /dev/null @@ -1,105 +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_send_window_sizes_piggyback_test - -import ( - "flag" - "fmt" - "testing" - "time" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -// TestSendWindowSizesPiggyback tests cases where segment sizes are close to -// sender window size and checks for ACK piggybacking for each of those case. -func TestSendWindowSizesPiggyback(t *testing.T) { - sampleData := []byte("Sample Data") - segmentSize := uint16(len(sampleData)) - // Advertise receive window sizes that are lesser, equal to or greater than - // enqueued segment size and check for segment transmits. The test attempts - // to enqueue a segment on the dut before acknowledging previous segment and - // lets the dut piggyback any ACKs along with the enqueued segment. - for _, tt := range []struct { - description string - windowSize uint16 - expectedPayload1 []byte - expectedPayload2 []byte - enqueue bool - }{ - // Expect the first segment to be split as it cannot be accomodated in - // the sender window. This means we need not enqueue a new segment after - // the first segment. - {"WindowSmallerThanSegment", segmentSize - 1, sampleData[:(segmentSize - 1)], sampleData[(segmentSize - 1):], false /* enqueue */}, - - {"WindowEqualToSegment", segmentSize, sampleData, sampleData, true /* enqueue */}, - - // Expect the second segment to not be split as its size is greater than - // the available sender window size. The segments should not be split - // when there is pending unacknowledged data and the segment-size is - // greater than available sender window. - {"WindowGreaterThanSegment", segmentSize + 1, sampleData, sampleData, true /* enqueue */}, - } { - t.Run(fmt.Sprintf("%s%d", tt.description, tt.windowSize), func(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFd) - - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort, WindowSize: testbench.Uint16(tt.windowSize)}, testbench.TCP{SrcPort: &remotePort}) - defer conn.Close(t) - - conn.Connect(t) - acceptFd, _ := dut.Accept(t, listenFd) - defer dut.Close(t, acceptFd) - - dut.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1) - - expectedTCP := testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)} - - dut.Send(t, acceptFd, sampleData, 0) - expectedPayload := testbench.Payload{Bytes: tt.expectedPayload1} - if _, err := conn.ExpectData(t, &expectedTCP, &expectedPayload, time.Second); err != nil { - t.Fatalf("expected payload was not received: %s", err) - } - - // Expect any enqueued segment to be transmitted by the dut along with - // piggybacked ACK for our data. - - if tt.enqueue { - // Enqueue a segment for the dut to transmit. - dut.Send(t, acceptFd, sampleData, 0) - } - - // 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}) - - // Expect the dut to piggyback the ACK for received data along with - // the segment enqueued for transmit. - expectedPayload = testbench.Payload{Bytes: tt.expectedPayload2} - if _, err := conn.ExpectData(t, &expectedTCP, &expectedPayload, time.Second); err != nil { - t.Fatalf("expected payload was not received: %s", err) - } - }) - } -} diff --git a/test/packetimpact/tests/tcp_synrcvd_reset_test.go b/test/packetimpact/tests/tcp_synrcvd_reset_test.go deleted file mode 100644 index 57d034dd1..000000000 --- a/test/packetimpact/tests/tcp_synrcvd_reset_test.go +++ /dev/null @@ -1,52 +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_syn_reset_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.RegisterFlags(flag.CommandLine) -} - -// TestTCPSynRcvdReset tests transition from SYN-RCVD to CLOSED. -func TestTCPSynRcvdReset(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFD) - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) - 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 { - t.Fatalf("expected SYN-ACK %s", err) - } - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(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 { - t.Fatalf("expected a TCP RST %s", err) - } -} diff --git a/test/packetimpact/tests/tcp_synsent_reset_test.go b/test/packetimpact/tests/tcp_synsent_reset_test.go deleted file mode 100644 index eac8eb19d..000000000 --- a/test/packetimpact/tests/tcp_synsent_reset_test.go +++ /dev/null @@ -1,90 +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_synsent_reset_test - -import ( - "flag" - "net" - "testing" - "time" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip/header" - tb "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - tb.RegisterFlags(flag.CommandLine) -} - -// dutSynSentState sets up the dut connection in SYN-SENT state. -func dutSynSentState(t *testing.T) (*tb.DUT, *tb.TCPIPv4, uint16, uint16) { - t.Helper() - - dut := tb.NewDUT(t) - - clientFD, clientPort := dut.CreateBoundSocket(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, net.ParseIP(tb.RemoteIPv4)) - port := uint16(9001) - conn := tb.NewTCPIPv4(t, tb.TCP{SrcPort: &port, DstPort: &clientPort}, tb.TCP{SrcPort: &clientPort, DstPort: &port}) - - sa := unix.SockaddrInet4{Port: int(port)} - copy(sa.Addr[:], net.IP(net.ParseIP(tb.LocalIPv4)).To4()) - // Bring the dut to SYN-SENT state with a non-blocking connect. - dut.Connect(t, clientFD, &sa) - if _, err := conn.ExpectData(t, &tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn)}, nil, time.Second); err != nil { - t.Fatalf("expected SYN\n") - } - - return &dut, &conn, port, clientPort -} - -// TestTCPSynSentReset tests RFC793, p67: SYN-SENT to CLOSED transition. -func TestTCPSynSentReset(t *testing.T) { - dut, conn, _, _ := dutSynSentState(t) - defer conn.Close(t) - defer dut.TearDown() - conn.Send(t, tb.TCP{Flags: tb.Uint8(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, tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}) - if _, err := conn.ExpectData(t, &tb.TCP{Flags: tb.Uint8(header.TCPFlagRst)}, nil, time.Second); err != nil { - t.Fatalf("expected a TCP RST") - } -} - -// TestTCPSynSentRcvdReset tests RFC793, p70, SYN-SENT to SYN-RCVD to CLOSED -// transitions. -func TestTCPSynSentRcvdReset(t *testing.T) { - dut, c, remotePort, clientPort := dutSynSentState(t) - defer dut.TearDown() - defer c.Close(t) - - conn := tb.NewTCPIPv4(t, tb.TCP{SrcPort: &remotePort, DstPort: &clientPort}, tb.TCP{SrcPort: &clientPort, DstPort: &remotePort}) - defer conn.Close(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, tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn)}) - if _, err := conn.ExpectData(t, &tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil { - t.Fatalf("expected SYN-ACK %s\n", err) - } - conn.Send(t, tb.TCP{Flags: tb.Uint8(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, tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}) - if _, err := conn.ExpectData(t, &tb.TCP{Flags: tb.Uint8(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 deleted file mode 100644 index 2f76a6531..000000000 --- a/test/packetimpact/tests/tcp_timewait_reset_test.go +++ /dev/null @@ -1,68 +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_timewait_reset_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.RegisterFlags(flag.CommandLine) -} - -// TestTimeWaitReset tests handling of RST when in TIME_WAIT state. -func TestTimeWaitReset(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/) - defer dut.Close(t, listenFD) - conn := testbench.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.Close(t, acceptFD) - - _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(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)}) - // 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 { - 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.Uint8(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 { - t.Fatalf("expected a RST: %s", err) - } -} diff --git a/test/packetimpact/tests/tcp_unacc_seq_ack_test.go b/test/packetimpact/tests/tcp_unacc_seq_ack_test.go deleted file mode 100644 index d078bbf15..000000000 --- a/test/packetimpact/tests/tcp_unacc_seq_ack_test.go +++ /dev/null @@ -1,234 +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_unacc_seq_ack_test - -import ( - "flag" - "fmt" - "syscall" - "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.RegisterFlags(flag.CommandLine) -} - -func TestEstablishedUnaccSeqAck(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 - 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}, - } { - t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/) - defer dut.Close(t, listenFD) - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) - defer conn.Close(t) - - conn.Connect(t) - dut.Accept(t, listenFD) - - 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) - if err != nil { - t.Fatalf("expected ack %s", err) - } - windowSize := seqnum.Size(*gotTCP.WindowSize) - - origSeq := *conn.LocalSeqNum(t) - // Send a segment with OTW Seq / unacc ACK. - conn.Send(t, tt.makeTestingTCP(t, &conn, tt.seqNumOffset, windowSize), samplePayload) - if tt.restoreSeq { - // Restore the local sequence number to ensure that the incoming - // ACK matches the TCP layer state. - *conn.LocalSeqNum(t) = origSeq - } - gotAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second) - if tt.expectAck && err != nil { - t.Fatalf("expected an ack but got none: %s", err) - } - if err == nil && !tt.expectAck && gotAck != nil { - t.Fatalf("expected no ack but got one: %s", gotAck) - } - }) - } -} - -func TestPassiveCloseUnaccSeqAck(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: 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}, - } { - t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/) - defer dut.Close(t, listenFD) - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) - defer conn.Close(t) - - conn.Connect(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) - if err != nil { - t.Fatalf("expected an ACK for our fin and DUT should enter CLOSE_WAIT: %s", err) - } - windowSize := seqnum.Size(*gotTCP.WindowSize) - - sampleData := []byte("Sample Data") - samplePayload := &testbench.Payload{Bytes: sampleData} - - // 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) - if tt.expectAck && err != nil { - t.Errorf("expected an ack but got none: %s", err) - } - if err == nil && !tt.expectAck && gotAck != nil { - t.Errorf("expected no ack but got one: %s", gotAck) - } - - // 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 { - 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)}) - // 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 { - t.Fatalf("expected DUT to send an RST: %s", err) - } - }) - } -} - -func TestActiveCloseUnaccpSeqAck(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 - 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}, - } { - t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/) - defer dut.Close(t, listenFD) - conn := testbench.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, syscall.SHUT_WR) - - // Get to FIN_WAIT2 - gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(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)}) - - sendUnaccSeqAck := func(state string) { - t.Helper() - sampleData := []byte("Sample Data") - samplePayload := &testbench.Payload{Bytes: sampleData} - - origSeq := *conn.LocalSeqNum(t) - // Send a segment with OTW Seq / unacc ACK. - conn.Send(t, tt.makeTestingTCP(t, &conn, tt.seqNumOffset, seqnum.Size(*gotTCP.WindowSize)), samplePayload) - if tt.restoreSeq { - // Restore the local sequence number to ensure that the - // 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 { - t.Errorf("expected an ack in %s state, but got none: %s", state, err) - } - } - - 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 { - t.Fatalf("expected an ACK for our fin and DUT should enter TIME_WAIT: %s", err) - } - - sendUnaccSeqAck("TIME_WAIT") - }) - } -} - -// 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 deleted file mode 100644 index 551dc78e7..000000000 --- a/test/packetimpact/tests/tcp_user_timeout_test.go +++ /dev/null @@ -1,100 +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_user_timeout_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.RegisterFlags(flag.CommandLine) -} - -func sendPayload(t *testing.T, conn *testbench.TCPIPv4, dut *testbench.DUT, fd int32) { - sampleData := make([]byte, 100) - for i := range sampleData { - sampleData[i] = uint8(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 { - t.Fatalf("expected data but got none: %w", err) - } -} - -func sendFIN(t *testing.T, conn *testbench.TCPIPv4, dut *testbench.DUT, fd int32) { - dut.Close(t, fd) -} - -func TestTCPUserTimeout(t *testing.T) { - for _, tt := range []struct { - description string - userTimeout time.Duration - sendDelay time.Duration - }{ - {"NoUserTimeout", 0, 3 * time.Second}, - {"ACKBeforeUserTimeout", 5 * time.Second, 4 * time.Second}, - {"ACKAfterUserTimeout", 5 * time.Second, 7 * time.Second}, - } { - for _, ttf := range []struct { - description string - f func(_ *testing.T, _ *testbench.TCPIPv4, _ *testbench.DUT, fd int32) - }{ - {"AfterPayload", sendPayload}, - {"AfterFIN", sendFIN}, - } { - t.Run(tt.description+ttf.description, func(t *testing.T) { - // Create a socket, listen, TCP handshake, and accept. - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFD) - conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) - defer conn.Close(t) - conn.Connect(t) - acceptFD, _ := dut.Accept(t, listenFD) - - if tt.userTimeout != 0 { - dut.SetSockOptInt(t, acceptFD, unix.SOL_TCP, unix.TCP_USER_TIMEOUT, int32(tt.userTimeout.Milliseconds())) - } - - ttf.f(t, &conn, &dut, acceptFD) - - time.Sleep(tt.sendDelay) - conn.Drain(t) - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(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) - if expectRST && err != nil { - t.Errorf("expected RST packet within %s but got none: %s", expectTimeout, err) - } - if !expectRST && got != nil { - t.Errorf("expected no RST packet within %s but got one: %s", expectTimeout, got) - } - }) - } - } -} diff --git a/test/packetimpact/tests/tcp_window_shrink_test.go b/test/packetimpact/tests/tcp_window_shrink_test.go deleted file mode 100644 index 5b001fbec..000000000 --- a/test/packetimpact/tests/tcp_window_shrink_test.go +++ /dev/null @@ -1,73 +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_window_shrink_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.RegisterFlags(flag.CommandLine) -} - -func TestWindowShrink(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFd) - conn := testbench.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.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1) - - sampleData := []byte("Sample Data") - samplePayload := &testbench.Payload{Bytes: sampleData} - - 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) - } - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}) - - dut.Send(t, acceptFd, sampleData, 0) - 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) - } - if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil { - 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)}) - - dut.Send(t, acceptFd, []byte("Sample Data"), 0) - // Note: There is another kind of zero-window probing which Windows uses (by sending one - // new byte at `RemoteSeqNum`), if netstack wants to go that way, we may want to change - // the following lines. - expectedRemoteSeqNum := *conn.RemoteSeqNum(t) - 1 - if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: testbench.Uint32(uint32(expectedRemoteSeqNum))}, nil, time.Second); err != nil { - t.Fatalf("expected a packet with sequence number %d: %s", expectedRemoteSeqNum, 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 deleted file mode 100644 index da93267d6..000000000 --- a/test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go +++ /dev/null @@ -1,104 +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_zero_window_probe_retransmit_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.RegisterFlags(flag.CommandLine) -} - -// TestZeroWindowProbeRetransmit tests retransmits of zero window probes -// to be sent at exponentially inreasing time intervals. -func TestZeroWindowProbeRetransmit(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFd) - conn := testbench.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.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1) - - sampleData := []byte("Sample Data") - samplePayload := &testbench.Payload{Bytes: sampleData} - - // Send and receive sample data to the dut. - 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) - } - 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)}) - probeSeq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t) - 1)) - ackProbe := testbench.Uint32(uint32(*conn.RemoteSeqNum(t))) - - startProbeDuration := time.Second - current := startProbeDuration - first := time.Now() - // Ask the dut to send out data. - dut.Send(t, acceptFd, sampleData, 0) - // Expect the dut to keep the connection alive as long as the remote is - // acknowledging the zero-window probes. - for i := 0; i < 5; i++ { - start := time.Now() - // Expect zero-window probe with a timeout which is a function of the typical - // first retransmission time. The retransmission times is supposed to - // exponentially increase. - if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: probeSeq}, nil, 2*current); err != nil { - t.Fatalf("expected a probe with sequence number %d: loop %d", probeSeq, i) - } - if i == 0 { - startProbeDuration = time.Now().Sub(first) - current = 2 * startProbeDuration - continue - } - // Check if the probes came at exponentially increasing intervals. - if got, want := time.Since(start), current-startProbeDuration; got < want { - t.Errorf("got zero probe %d after %s, want >= %s", i, got, want) - } - // Acknowledge the zero-window probes from the dut. - conn.Send(t, testbench.TCP{AckNum: ackProbe, Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)}) - current *= 2 - } - // Advertize non-zero window. - conn.Send(t, testbench.TCP{AckNum: ackProbe, Flags: testbench.Uint8(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 deleted file mode 100644 index 44cac42f8..000000000 --- a/test/packetimpact/tests/tcp_zero_window_probe_test.go +++ /dev/null @@ -1,112 +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_zero_window_probe_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.RegisterFlags(flag.CommandLine) -} - -// TestZeroWindowProbe tests few cases of zero window probing over the -// same connection. -func TestZeroWindowProbe(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFd) - conn := testbench.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.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1) - - sampleData := []byte("Sample Data") - samplePayload := &testbench.Payload{Bytes: sampleData} - - start := time.Now() - // Send and receive sample data to the dut. - 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) - } - 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 { - t.Fatalf("expected packet was not received: %s", err) - } - - // Test 1: Check for receive of a zero window probe, record the duration for - // probe to be sent. - // - // Advertize zero window to the dut. - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)}) - - // Expected sequence number of the zero window probe. - probeSeq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t) - 1)) - // Expected ack number of the ACK for the probe. - ackProbe := testbench.Uint32(uint32(*conn.RemoteSeqNum(t))) - - // Expect there are no zero-window probes sent until there is data to be sent out - // from the dut. - if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: probeSeq}, nil, 2*time.Second); err == nil { - t.Fatalf("unexpected packet with sequence number %d: %s", probeSeq, err) - } - - start = time.Now() - // Ask the dut to send out data. - dut.Send(t, acceptFd, sampleData, 0) - // Expect zero-window probe from the dut. - if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: probeSeq}, nil, time.Second); err != nil { - t.Fatalf("expected a packet with sequence number %d: %s", probeSeq, err) - } - // Expect the probe to be sent after some time. Compare against the previous - // time recorded when the dut immediately sends out data on receiving the - // send command. - if startProbeDuration := time.Now().Sub(start); startProbeDuration <= sendTime { - t.Fatalf("expected the first probe to be sent out after retransmission interval, got %s want > %s", startProbeDuration, sendTime) - } - - // Test 2: Check if the dut recovers on advertizing non-zero receive window. - // 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)}) - // 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) - } - - // Test 3: Sanity check for dut's processing of a similar probe it sent. - // Check if the dut responds as we do for a similar probe sent to it. - // 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 { - 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 deleted file mode 100644 index 09a1c653f..000000000 --- a/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go +++ /dev/null @@ -1,98 +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_zero_window_probe_usertimeout_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.RegisterFlags(flag.CommandLine) -} - -// TestZeroWindowProbeUserTimeout sanity tests user timeout when we are -// retransmitting zero window probes. -func TestZeroWindowProbeUserTimeout(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) - defer dut.Close(t, listenFd) - conn := testbench.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.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1) - - sampleData := []byte("Sample Data") - samplePayload := &testbench.Payload{Bytes: sampleData} - - // Send and receive sample data to the dut. - 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) - } - 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) - } - - // Test 1: Check for receive of a zero window probe, record the duration for - // probe to be sent. - // - // Advertize zero window to the dut. - conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)}) - - // Expected sequence number of the zero window probe. - probeSeq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t) - 1)) - start := time.Now() - // Ask the dut to send out data. - dut.Send(t, acceptFd, sampleData, 0) - // Expect zero-window probe from the dut. - if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: probeSeq}, nil, time.Second); err != nil { - t.Fatalf("expected a packet with sequence number %d: %s", probeSeq, err) - } - // Record the duration for first probe, the dut sends the zero window probe after - // a retransmission time interval. - startProbeDuration := time.Now().Sub(start) - - // Test 2: Check if the dut times out the connection by honoring usertimeout - // when the dut is sending zero-window probes. - // - // 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)}) - // Ask the dut to send out data that would trigger zero window probe retransmissions. - dut.Send(t, acceptFd, sampleData, 0) - - // Wait for the connection to timeout after multiple zero-window probe retransmissions. - time.Sleep(8 * startProbeDuration) - - // 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 { - t.Fatalf("expected a TCP RST") - } -} diff --git a/test/packetimpact/tests/udp_any_addr_recv_unicast_test.go b/test/packetimpact/tests/udp_any_addr_recv_unicast_test.go deleted file mode 100644 index 17f32ef65..000000000 --- a/test/packetimpact/tests/udp_any_addr_recv_unicast_test.go +++ /dev/null @@ -1,51 +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 udp_any_addr_recv_unicast_test - -import ( - "flag" - "net" - "testing" - - "github.com/google/go-cmp/cmp" - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -func TestAnyRecvUnicastUDP(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - boundFD, remotePort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.IPv4zero) - defer dut.Close(t, boundFD) - conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort}) - defer conn.Close(t) - - payload := testbench.GenerateRandomPayload(t, 1<<10 /* 1 KiB */) - conn.SendIP( - t, - testbench.IPv4{DstAddr: testbench.Address(tcpip.Address(net.ParseIP(testbench.RemoteIPv4).To4()))}, - testbench.UDP{}, - &testbench.Payload{Bytes: payload}, - ) - got, want := dut.Recv(t, boundFD, int32(len(payload)+1), 0), payload - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("received payload does not match sent payload, diff (-want, +got):\n%s", diff) - } -} diff --git a/test/packetimpact/tests/udp_discard_mcast_source_addr_test.go b/test/packetimpact/tests/udp_discard_mcast_source_addr_test.go deleted file mode 100644 index 3d2791a6e..000000000 --- a/test/packetimpact/tests/udp_discard_mcast_source_addr_test.go +++ /dev/null @@ -1,96 +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 udp_discard_mcast_source_addr_test - -import ( - "context" - "flag" - "fmt" - "net" - "syscall" - "testing" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -var oneSecond = unix.Timeval{Sec: 1, Usec: 0} - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -func TestDiscardsUDPPacketsWithMcastSourceAddressV4(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - remoteFD, remotePort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.ParseIP(testbench.RemoteIPv4)) - defer dut.Close(t, remoteFD) - dut.SetSockOptTimeval(t, remoteFD, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &oneSecond) - conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort}) - defer conn.Close(t) - - for _, mcastAddr := range []net.IP{ - net.IPv4allsys, - net.IPv4allrouter, - net.IPv4(224, 0, 1, 42), - net.IPv4(232, 1, 2, 3), - } { - t.Run(fmt.Sprintf("srcaddr=%s", mcastAddr), func(t *testing.T) { - conn.SendIP( - t, - testbench.IPv4{SrcAddr: testbench.Address(tcpip.Address(mcastAddr.To4()))}, - testbench.UDP{}, - &testbench.Payload{Bytes: []byte("test payload")}, - ) - - ret, payload, errno := dut.RecvWithErrno(context.Background(), t, remoteFD, 100, 0) - if errno != syscall.EAGAIN || errno != syscall.EWOULDBLOCK { - t.Errorf("Recv got unexpected result, ret=%d, payload=%q, errno=%s", ret, payload, errno) - } - }) - } -} - -func TestDiscardsUDPPacketsWithMcastSourceAddressV6(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - remoteFD, remotePort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.ParseIP(testbench.RemoteIPv6)) - defer dut.Close(t, remoteFD) - dut.SetSockOptTimeval(t, remoteFD, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &oneSecond) - conn := testbench.NewUDPIPv6(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort}) - defer conn.Close(t) - - for _, mcastAddr := range []net.IP{ - net.IPv6interfacelocalallnodes, - net.IPv6linklocalallnodes, - net.IPv6linklocalallrouters, - net.ParseIP("ff01::42"), - net.ParseIP("ff02::4242"), - } { - t.Run(fmt.Sprintf("srcaddr=%s", mcastAddr), func(t *testing.T) { - conn.SendIPv6( - t, - testbench.IPv6{SrcAddr: testbench.Address(tcpip.Address(mcastAddr.To16()))}, - testbench.UDP{}, - &testbench.Payload{Bytes: []byte("test payload")}, - ) - ret, payload, errno := dut.RecvWithErrno(context.Background(), t, remoteFD, 100, 0) - if errno != syscall.EAGAIN || errno != syscall.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 deleted file mode 100644 index df35d16c8..000000000 --- a/test/packetimpact/tests/udp_icmp_error_propagation_test.go +++ /dev/null @@ -1,363 +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 udp_icmp_error_propagation_test - -import ( - "context" - "flag" - "fmt" - "net" - "sync" - "syscall" - "testing" - "time" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -type connectionMode bool - -func (c connectionMode) String() string { - if c { - return "Connected" - } - return "Connectionless" -} - -type icmpError int - -const ( - portUnreachable icmpError = iota - timeToLiveExceeded -) - -func (e icmpError) String() string { - switch e { - case portUnreachable: - return "PortUnreachable" - case timeToLiveExceeded: - return "TimeToLiveExpired" - } - return "Unknown ICMP error" -} - -func (e icmpError) ToICMPv4() *testbench.ICMPv4 { - switch e { - case portUnreachable: - return &testbench.ICMPv4{ - Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable), - Code: testbench.ICMPv4Code(header.ICMPv4PortUnreachable)} - case timeToLiveExceeded: - return &testbench.ICMPv4{ - Type: testbench.ICMPv4Type(header.ICMPv4TimeExceeded), - Code: testbench.ICMPv4Code(header.ICMPv4TTLExceeded)} - } - return nil -} - -type errorDetection struct { - name string - useValidConn bool - f func(context.Context, *testing.T, testData) -} - -type testData struct { - dut *testbench.DUT - conn *testbench.UDPIPv4 - remoteFD int32 - remotePort uint16 - cleanFD int32 - cleanPort uint16 - wantErrno syscall.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 { - if c && icmpErr == portUnreachable { - return syscall.Errno(unix.ECONNREFUSED) - } - return syscall.Errno(0) -} - -// sendICMPError sends an ICMP error message in response to a UDP datagram. -func sendICMPError(t *testing.T, conn *testbench.UDPIPv4, icmpErr icmpError, udp *testbench.UDP) { - t.Helper() - - layers := (*testbench.Connection)(conn).CreateFrame(t, nil) - layers = layers[:len(layers)-1] - ip, ok := udp.Prev().(*testbench.IPv4) - if !ok { - t.Fatalf("expected %s to be IPv4", udp.Prev()) - } - if icmpErr == timeToLiveExceeded { - *ip.TTL = 1 - // Let serialization recalculate the checksum since we set the TTL - // to 1. - ip.Checksum = nil - } - // Note that the ICMP payload is valid in this case because the UDP - // payload is empty. If the UDP payload were not empty, the packet - // length during serialization may not be calculated correctly, - // resulting in a mal-formed packet. - layers = append(layers, icmpErr.ToICMPv4(), ip, udp) - - (*testbench.Connection)(conn).SendFrameStateless(t, layers) -} - -// testRecv tests observing the ICMP error through the recv syscall. 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. -func testRecv(ctx context.Context, t *testing.T, d testData) { - t.Helper() - - // Check that receiving on the clean socket works. - d.conn.Send(t, testbench.UDP{DstPort: &d.cleanPort}) - d.dut.Recv(t, d.cleanFD, 100, 0) - - d.conn.Send(t, testbench.UDP{}) - - if d.wantErrno != syscall.Errno(0) { - ctx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - ret, _, err := d.dut.RecvWithErrno(ctx, t, d.remoteFD, 100, 0) - if ret != -1 { - t.Fatalf("recv after ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", d.wantErrno) - } - if err != d.wantErrno { - t.Fatalf("recv after ICMP error resulted in error (%[1]d) %[1]v, expected (%[2]d) %[2]v", err, d.wantErrno) - } - } - - d.dut.Recv(t, d.remoteFD, 100, 0) -} - -// testSendTo tests observing the ICMP error through the send syscall. If -// wantErrno is non-zero, the first send should fail and a subsequent send -// should suceed; while if wantErrno is zero then the first send should just -// succeed. -func testSendTo(ctx context.Context, t *testing.T, d testData) { - // Check that sending on the clean socket works. - d.dut.SendTo(t, d.cleanFD, nil, 0, d.conn.LocalAddr(t)) - if _, err := d.conn.Expect(t, testbench.UDP{SrcPort: &d.cleanPort}, time.Second); err != nil { - t.Fatalf("did not receive UDP packet from clean socket on DUT: %s", err) - } - - if d.wantErrno != syscall.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)) - - if ret != -1 { - t.Fatalf("sendto after ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", d.wantErrno) - } - if err != d.wantErrno { - t.Fatalf("sendto after ICMP error resulted in error (%[1]d) %[1]v, expected (%[2]d) %[2]v", err, d.wantErrno) - } - } - - d.dut.SendTo(t, d.remoteFD, nil, 0, d.conn.LocalAddr(t)) - if _, err := d.conn.Expect(t, testbench.UDP{}, time.Second); err != nil { - t.Fatalf("did not receive UDP packet as expected: %s", err) - } -} - -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) { - 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 { - t.Fatalf("SO_ERROR sockopt after ICMP error is (%[1]d) %[1]v, expected (%[2]d) %[2]v", errno, d.wantErrno) - } - - // Check that after clearing socket error, sending doesn't fail. - d.dut.SendTo(t, d.remoteFD, nil, 0, d.conn.LocalAddr(t)) - if _, err := d.conn.Expect(t, testbench.UDP{}, time.Second); err != nil { - t.Fatalf("did not receive UDP packet as expected: %s", err) - } -} - -// TestUDPICMPErrorPropagation tests that ICMP error messages in response to -// UDP datagrams are processed correctly. RFC 1122 section 4.1.3.3 states that: -// "UDP MUST pass to the application layer all ICMP error messages that it -// receives from the IP layer." -// -// The test cases are parametrized in 3 dimensions: 1. the UDP socket is either -// put into connection mode or left connectionless, 2. the ICMP message type -// and code, and 3. the method by which the ICMP error is observed on the -// socket: sendto, recv, or getsockopt(SO_ERROR). -// -// Linux's udp(7) man page states: "All fatal errors will be passed to the user -// as an error return even when the socket is not connected. This includes -// asynchronous errors received from the network." In practice, the only -// combination of parameters to the test that causes an error to be observable -// on the UDP socket is receiving a port unreachable message on a connected -// socket. -func TestUDPICMPErrorPropagation(t *testing.T) { - for _, connect := range []connectionMode{true, false} { - for _, icmpErr := range []icmpError{portUnreachable, timeToLiveExceeded} { - wantErrno := wantErrno(connect, icmpErr) - - for _, errDetect := range []errorDetection{ - errorDetection{"SendTo", false, testSendTo}, - // Send to an address that's different from the one that caused an ICMP - // error to be returned. - errorDetection{"SendToValid", true, testSendTo}, - errorDetection{"Recv", false, testRecv}, - errorDetection{"SockOpt", false, testSockOpt}, - } { - t.Run(fmt.Sprintf("%s/%s/%s", connect, icmpErr, errDetect.name), func(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - - remoteFD, remotePort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.IPv4zero) - defer dut.Close(t, remoteFD) - - // Create a second, clean socket on the DUT to ensure that the ICMP - // error messages only affect the sockets they are intended for. - cleanFD, cleanPort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.IPv4zero) - defer dut.Close(t, cleanFD) - - conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort}) - defer conn.Close(t) - - if connect { - dut.Connect(t, remoteFD, conn.LocalAddr(t)) - dut.Connect(t, cleanFD, conn.LocalAddr(t)) - } - - dut.SendTo(t, remoteFD, nil, 0, conn.LocalAddr(t)) - udp, err := conn.Expect(t, testbench.UDP{}, time.Second) - if err != nil { - t.Fatalf("did not receive message from DUT: %s", err) - } - - sendICMPError(t, &conn, icmpErr, udp) - - errDetectConn := &conn - if errDetect.useValidConn { - // connClean is a UDP socket on the test runner that was not - // involved in the generation of the ICMP error. As such, - // interactions between it and the the DUT should be independent of - // the ICMP error at least at the port level. - connClean := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort}) - defer connClean.Close(t) - - errDetectConn = &connClean - } - - errDetect.f(context.Background(), t, testData{&dut, errDetectConn, remoteFD, remotePort, cleanFD, cleanPort, wantErrno}) - }) - } - } - } -} - -// TestICMPErrorDuringUDPRecv tests behavior when a UDP socket is in the middle -// of a blocking recv and receives an ICMP error. -func TestICMPErrorDuringUDPRecv(t *testing.T) { - for _, connect := range []connectionMode{true, false} { - for _, icmpErr := range []icmpError{portUnreachable, timeToLiveExceeded} { - wantErrno := wantErrno(connect, icmpErr) - - t.Run(fmt.Sprintf("%s/%s", connect, icmpErr), func(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - - remoteFD, remotePort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.IPv4zero) - defer dut.Close(t, remoteFD) - - // Create a second, clean socket on the DUT to ensure that the ICMP - // error messages only affect the sockets they are intended for. - cleanFD, cleanPort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.IPv4zero) - defer dut.Close(t, cleanFD) - - conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort}) - defer conn.Close(t) - - if connect { - dut.Connect(t, remoteFD, conn.LocalAddr(t)) - dut.Connect(t, cleanFD, conn.LocalAddr(t)) - } - - dut.SendTo(t, remoteFD, nil, 0, conn.LocalAddr(t)) - udp, err := conn.Expect(t, testbench.UDP{}, time.Second) - if err != nil { - t.Fatalf("did not receive message from DUT: %s", err) - } - - var wg sync.WaitGroup - wg.Add(2) - go func() { - defer wg.Done() - - if wantErrno != syscall.Errno(0) { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - ret, _, err := dut.RecvWithErrno(ctx, t, remoteFD, 100, 0) - if ret != -1 { - t.Errorf("recv during ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", wantErrno) - return - } - if err != wantErrno { - t.Errorf("recv during ICMP error resulted in error (%[1]d) %[1]v, expected (%[2]d) %[2]v", err, wantErrno) - return - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - if ret, _, err := dut.RecvWithErrno(ctx, t, remoteFD, 100, 0); ret == -1 { - t.Errorf("recv after ICMP error failed with (%[1]d) %[1]", err) - } - }() - - go func() { - defer wg.Done() - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - if ret, _, err := dut.RecvWithErrno(ctx, t, cleanFD, 100, 0); ret == -1 { - t.Errorf("recv on clean socket failed with (%[1]d) %[1]", err) - } - }() - - // TODO(b/155684889) This sleep is to allow time for the DUT to - // actually call recv since we want the ICMP error to arrive during the - // blocking recv, and should be replaced when a better synchronization - // alternative is available. - time.Sleep(2 * time.Second) - - sendICMPError(t, &conn, icmpErr, udp) - - conn.Send(t, testbench.UDP{DstPort: &cleanPort}) - conn.Send(t, testbench.UDP{}) - wg.Wait() - }) - } - } -} diff --git a/test/packetimpact/tests/udp_recv_mcast_bcast_test.go b/test/packetimpact/tests/udp_recv_mcast_bcast_test.go deleted file mode 100644 index 526173969..000000000 --- a/test/packetimpact/tests/udp_recv_mcast_bcast_test.go +++ /dev/null @@ -1,110 +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 udp_recv_mcast_bcast_test - -import ( - "context" - "flag" - "fmt" - "net" - "syscall" - "testing" - - "github.com/google/go-cmp/cmp" - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -func TestUDPRecvMcastBcast(t *testing.T) { - subnetBcastAddr := broadcastAddr(net.ParseIP(testbench.RemoteIPv4), net.CIDRMask(testbench.IPv4PrefixLength, 32)) - - for _, v := range []struct { - bound, to net.IP - }{ - {bound: net.IPv4zero, to: subnetBcastAddr}, - {bound: net.IPv4zero, to: net.IPv4bcast}, - {bound: net.IPv4zero, to: net.IPv4allsys}, - - {bound: subnetBcastAddr, to: subnetBcastAddr}, - {bound: subnetBcastAddr, to: net.IPv4bcast}, - - {bound: net.IPv4bcast, to: net.IPv4bcast}, - {bound: net.IPv4allsys, to: net.IPv4allsys}, - } { - t.Run(fmt.Sprintf("bound=%s,to=%s", v.bound, v.to), func(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - boundFD, remotePort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, v.bound) - defer dut.Close(t, boundFD) - conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort}) - defer conn.Close(t) - - payload := testbench.GenerateRandomPayload(t, 1<<10 /* 1 KiB */) - conn.SendIP( - t, - testbench.IPv4{DstAddr: testbench.Address(tcpip.Address(v.to.To4()))}, - testbench.UDP{}, - &testbench.Payload{Bytes: payload}, - ) - got, want := dut.Recv(t, boundFD, int32(len(payload)+1), 0), payload - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("received payload does not match sent payload, diff (-want, +got):\n%s", diff) - } - }) - } -} - -func TestUDPDoesntRecvMcastBcastOnUnicastAddr(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - boundFD, remotePort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.ParseIP(testbench.RemoteIPv4)) - dut.SetSockOptTimeval(t, boundFD, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &unix.Timeval{Sec: 1, Usec: 0}) - defer dut.Close(t, boundFD) - conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort}) - defer conn.Close(t) - - for _, to := range []net.IP{ - broadcastAddr(net.ParseIP(testbench.RemoteIPv4), net.CIDRMask(testbench.IPv4PrefixLength, 32)), - net.IPv4(255, 255, 255, 255), - net.IPv4(224, 0, 0, 1), - } { - t.Run(fmt.Sprint("to=%s", to), func(t *testing.T) { - payload := testbench.GenerateRandomPayload(t, 1<<10 /* 1 KiB */) - conn.SendIP( - t, - testbench.IPv4{DstAddr: testbench.Address(tcpip.Address(to.To4()))}, - testbench.UDP{}, - &testbench.Payload{Bytes: payload}, - ) - ret, payload, errno := dut.RecvWithErrno(context.Background(), t, boundFD, 100, 0) - if errno != syscall.EAGAIN || errno != syscall.EWOULDBLOCK { - t.Errorf("Recv got unexpected result, ret=%d, payload=%q, errno=%s", ret, payload, errno) - } - }) - } -} - -func broadcastAddr(ip net.IP, mask net.IPMask) net.IP { - ip4 := ip.To4() - for i := range ip4 { - ip4[i] |= ^mask[i] - } - return ip4 -} diff --git a/test/packetimpact/tests/udp_send_recv_dgram_test.go b/test/packetimpact/tests/udp_send_recv_dgram_test.go deleted file mode 100644 index 91b967400..000000000 --- a/test/packetimpact/tests/udp_send_recv_dgram_test.go +++ /dev/null @@ -1,104 +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 udp_send_recv_dgram_test - -import ( - "flag" - "net" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/test/packetimpact/testbench" -) - -func init() { - testbench.RegisterFlags(flag.CommandLine) -} - -type udpConn interface { - Send(*testing.T, testbench.UDP, ...testbench.Layer) - ExpectData(*testing.T, testbench.UDP, testbench.Payload, time.Duration) (testbench.Layers, error) - Drain(*testing.T) - Close(*testing.T) -} - -func TestUDP(t *testing.T) { - dut := testbench.NewDUT(t) - defer dut.TearDown() - - for _, isIPv4 := range []bool{true, false} { - ipVersionName := "IPv6" - if isIPv4 { - ipVersionName = "IPv4" - } - t.Run(ipVersionName, func(t *testing.T) { - var addr string - if isIPv4 { - addr = testbench.RemoteIPv4 - } else { - addr = testbench.RemoteIPv6 - } - boundFD, remotePort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.ParseIP(addr)) - defer dut.Close(t, boundFD) - - var conn udpConn - var localAddr unix.Sockaddr - if isIPv4 { - v4Conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort}) - localAddr = v4Conn.LocalAddr(t) - conn = &v4Conn - } else { - v6Conn := testbench.NewUDPIPv6(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort}) - localAddr = v6Conn.LocalAddr(t) - conn = &v6Conn - } - defer conn.Close(t) - - testCases := []struct { - name string - payload []byte - }{ - {"emptypayload", nil}, - {"small payload", []byte("hello world")}, - {"1kPayload", testbench.GenerateRandomPayload(t, 1<<10)}, - // Even though UDP allows larger dgrams we don't test it here as - // they need to be fragmented and written out as individual - // frames. - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - t.Run("Send", func(t *testing.T) { - conn.Send(t, testbench.UDP{}, &testbench.Payload{Bytes: tc.payload}) - got, want := dut.Recv(t, boundFD, int32(len(tc.payload)+1), 0), tc.payload - if diff := cmp.Diff(want, got); diff != "" { - t.Fatalf("received payload does not match sent payload, diff (-want, +got):\n%s", diff) - } - }) - t.Run("Recv", func(t *testing.T) { - conn.Drain(t) - if got, want := int(dut.SendTo(t, boundFD, tc.payload, 0, localAddr)), len(tc.payload); got != want { - t.Fatalf("short write got: %d, want: %d", got, want) - } - if _, err := conn.ExpectData(t, testbench.UDP{SrcPort: &remotePort}, testbench.Payload{Bytes: tc.payload}, time.Second); err != nil { - t.Fatal(err) - } - }) - }) - } - }) - } -} |