summaryrefslogtreecommitdiffhomepage
path: root/test/packetimpact/tests
diff options
context:
space:
mode:
Diffstat (limited to 'test/packetimpact/tests')
-rw-r--r--test/packetimpact/tests/BUILD431
-rw-r--r--test/packetimpact/tests/fin_wait2_timeout_test.go74
-rw-r--r--test/packetimpact/tests/generic_dgram_socket_send_recv_test.go781
-rw-r--r--test/packetimpact/tests/icmpv6_param_problem_test.go74
-rw-r--r--test/packetimpact/tests/ipv4_fragment_reassembly_test.go175
-rw-r--r--test/packetimpact/tests/ipv4_id_uniqueness_test.go121
-rw-r--r--test/packetimpact/tests/ipv6_fragment_icmp_error_test.go356
-rw-r--r--test/packetimpact/tests/ipv6_fragment_reassembly_test.go182
-rw-r--r--test/packetimpact/tests/ipv6_unknown_options_action_test.go183
-rw-r--r--test/packetimpact/tests/tcp_connect_icmp_error_test.go106
-rw-r--r--test/packetimpact/tests/tcp_cork_mss_test.go83
-rw-r--r--test/packetimpact/tests/tcp_fin_retransmission_test.go87
-rw-r--r--test/packetimpact/tests/tcp_handshake_window_size_test.go65
-rw-r--r--test/packetimpact/tests/tcp_info_test.go102
-rw-r--r--test/packetimpact/tests/tcp_linger_test.go265
-rw-r--r--test/packetimpact/tests/tcp_listen_backlog_test.go308
-rw-r--r--test/packetimpact/tests/tcp_network_unreachable_test.go144
-rw-r--r--test/packetimpact/tests/tcp_noaccept_close_rst_test.go46
-rw-r--r--test/packetimpact/tests/tcp_outside_the_window_test.go182
-rw-r--r--test/packetimpact/tests/tcp_paws_mechanism_test.go108
-rw-r--r--test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go286
-rw-r--r--test/packetimpact/tests/tcp_rack_test.go404
-rw-r--r--test/packetimpact/tests/tcp_rcv_buf_space_test.go78
-rw-r--r--test/packetimpact/tests/tcp_retransmits_test.go94
-rw-r--r--test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go103
-rw-r--r--test/packetimpact/tests/tcp_syncookie_test.go151
-rw-r--r--test/packetimpact/tests/tcp_synrcvd_reset_test.go60
-rw-r--r--test/packetimpact/tests/tcp_synsent_reset_test.go94
-rw-r--r--test/packetimpact/tests/tcp_timewait_reset_test.go67
-rw-r--r--test/packetimpact/tests/tcp_unacc_seq_ack_test.go274
-rw-r--r--test/packetimpact/tests/tcp_user_timeout_test.go99
-rw-r--r--test/packetimpact/tests/tcp_window_shrink_test.go72
-rw-r--r--test/packetimpact/tests/tcp_zero_receive_window_test.go202
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go115
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_test.go111
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go97
-rw-r--r--test/packetimpact/tests/udp_any_addr_recv_unicast_test.go50
-rw-r--r--test/packetimpact/tests/udp_discard_mcast_source_addr_test.go93
-rw-r--r--test/packetimpact/tests/udp_icmp_error_propagation_test.go352
39 files changed, 0 insertions, 6675 deletions
diff --git a/test/packetimpact/tests/BUILD b/test/packetimpact/tests/BUILD
deleted file mode 100644
index 4cff0cf4c..000000000
--- a/test/packetimpact/tests/BUILD
+++ /dev/null
@@ -1,431 +0,0 @@
-load("//test/packetimpact/runner:defs.bzl", "ALL_TESTS", "packetimpact_go_test", "packetimpact_testbench", "validate_all_tests")
-
-package(
- default_visibility = ["//test/packetimpact:__subpackages__"],
- licenses = ["notice"],
-)
-
-packetimpact_testbench(
- 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_testbench(
- 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_testbench(
- 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_testbench(
- 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_testbench(
- 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_testbench(
- 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_testbench(
- 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_testbench(
- 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_testbench(
- 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_testbench(
- name = "tcp_retransmits",
- srcs = ["tcp_retransmits_test.go"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- 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_testbench(
- 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_testbench(
- 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_testbench(
- 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_testbench(
- name = "tcp_paws_mechanism",
- srcs = ["tcp_paws_mechanism_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//pkg/tcpip/seqnum",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- 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_testbench(
- name = "tcp_queue_send_recv_in_syn_sent",
- srcs = ["tcp_queue_send_recv_in_syn_sent_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "tcp_synsent_reset",
- srcs = ["tcp_synsent_reset_test.go"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- 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_testbench(
- 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_testbench(
- 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_testbench(
- 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_testbench(
- name = "tcp_timewait_reset",
- srcs = ["tcp_timewait_reset_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "icmpv6_param_problem",
- srcs = ["icmpv6_param_problem_test.go"],
- deps = [
- "//pkg/tcpip",
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "ipv6_unknown_options_action",
- srcs = ["ipv6_unknown_options_action_test.go"],
- deps = [
- "//pkg/tcpip",
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "ipv4_fragment_reassembly",
- srcs = ["ipv4_fragment_reassembly_test.go"],
- deps = [
- "//pkg/tcpip/buffer",
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@com_github_google_go_cmp//cmp:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "ipv6_fragment_reassembly",
- srcs = ["ipv6_fragment_reassembly_test.go"],
- deps = [
- "//pkg/tcpip",
- "//pkg/tcpip/buffer",
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@com_github_google_go_cmp//cmp:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "ipv6_fragment_icmp_error",
- srcs = ["ipv6_fragment_icmp_error_test.go"],
- deps = [
- "//pkg/tcpip",
- "//pkg/tcpip/buffer",
- "//pkg/tcpip/header",
- "//pkg/tcpip/network/ipv6",
- "//test/packetimpact/testbench",
- "@com_github_google_go_cmp//cmp:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "tcp_linger",
- srcs = ["tcp_linger_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "tcp_rcv_buf_space",
- srcs = ["tcp_rcv_buf_space_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "tcp_zero_receive_window",
- srcs = ["tcp_zero_receive_window_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "tcp_rack",
- srcs = ["tcp_rack_test.go"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/tcpip/header",
- "//pkg/tcpip/seqnum",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "tcp_info",
- srcs = ["tcp_info_test.go"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "tcp_fin_retransmission",
- srcs = ["tcp_fin_retransmission_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "tcp_listen_backlog",
- srcs = ["tcp_listen_backlog_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "tcp_syncookie",
- srcs = ["tcp_syncookie_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@com_github_google_go_cmp//cmp:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "tcp_connect_icmp_error",
- srcs = ["tcp_connect_icmp_error_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_testbench(
- name = "generic_dgram_socket_send_recv",
- srcs = ["generic_dgram_socket_send_recv_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",
- ],
-)
-
-validate_all_tests()
-
-[packetimpact_go_test(
- name = t.name,
- timeout = t.timeout if hasattr(t, "timeout") else "moderate",
- expect_netstack_failure = hasattr(t, "expect_netstack_failure"),
- num_duts = t.num_duts if hasattr(t, "num_duts") else 1,
-) for t in ALL_TESTS]
-
-test_suite(
- name = "all_tests",
- tags = [
- "local",
- "manual",
- "packetimpact",
- ],
- tests = existing_rules(),
-)
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 cff8ca51d..000000000
--- a/test/packetimpact/tests/fin_wait2_timeout_test.go
+++ /dev/null
@@ -1,74 +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.Initialize(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)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
- conn.Connect(t)
-
- acceptFd, _ := dut.Accept(t, listenFd)
- 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.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected a FIN-ACK within 1 second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
-
- time.Sleep(5 * time.Second)
- conn.Drain(t)
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- if tt.linger2 {
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, time.Second); err != nil {
- t.Fatalf("expected a RST packet within a second but got none: %s", err)
- }
- } else {
- if got, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, 10*time.Second); got != nil || err == nil {
- t.Fatalf("expected no RST packets within ten seconds but got one: %s", got)
- }
- }
- })
- }
-}
diff --git a/test/packetimpact/tests/generic_dgram_socket_send_recv_test.go b/test/packetimpact/tests/generic_dgram_socket_send_recv_test.go
deleted file mode 100644
index a9ffafc74..000000000
--- a/test/packetimpact/tests/generic_dgram_socket_send_recv_test.go
+++ /dev/null
@@ -1,781 +0,0 @@
-// Copyright 2021 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package generic_dgram_socket_send_recv_test
-
-import (
- "context"
- "flag"
- "fmt"
- "net"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-const (
- // Even though sockets allow larger datagrams we don't test it here as they
- // need to be fragmented and written out as individual frames.
-
- maxICMPv4PayloadSize = header.IPv4MinimumMTU - header.EthernetMinimumSize - header.IPv4MinimumSize - header.ICMPv4MinimumSize
- maxICMPv6PayloadSize = header.IPv6MinimumMTU - header.EthernetMinimumSize - header.IPv6MinimumSize - header.ICMPv6MinimumSize
- maxUDPv4PayloadSize = header.IPv4MinimumMTU - header.EthernetMinimumSize - header.IPv4MinimumSize - header.UDPMinimumSize
- maxUDPv6PayloadSize = header.IPv6MinimumMTU - header.EthernetMinimumSize - header.IPv6MinimumSize - header.UDPMinimumSize
-)
-
-func maxUDPPayloadSize(addr net.IP) int {
- if addr.To4() != nil {
- return maxUDPv4PayloadSize
- }
- return maxUDPv6PayloadSize
-}
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-func expectedEthLayer(t *testing.T, dut testbench.DUT, socketFD int32, sendTo net.IP) testbench.Layer {
- t.Helper()
- dst := func() tcpip.LinkAddress {
- if isBroadcast(dut, sendTo) {
- dut.SetSockOptInt(t, socketFD, unix.SOL_SOCKET, unix.SO_BROADCAST, 1)
-
- // When sending to broadcast (subnet or limited), the expected ethernet
- // address is also broadcast.
- return header.EthernetBroadcastAddress
- }
- if sendTo.IsMulticast() {
- if sendTo4 := sendTo.To4(); sendTo4 != nil {
- return header.EthernetAddressFromMulticastIPv4Address(tcpip.Address(sendTo4))
- }
- return header.EthernetAddressFromMulticastIPv6Address(tcpip.Address(sendTo.To16()))
- }
- return ""
- }()
- var ether testbench.Ether
- if len(dst) != 0 {
- ether.DstAddr = &dst
- }
- return &ether
-}
-
-type protocolTest interface {
- Name() string
- Send(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP, bindToDevice bool)
- Receive(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP, bindToDevice bool)
-}
-
-func TestSocket(t *testing.T) {
- dut := testbench.NewDUT(t)
- subnetBroadcast := dut.Net.SubnetBroadcast()
-
- for _, proto := range []protocolTest{
- &icmpV4Test{},
- &icmpV6Test{},
- &udpTest{},
- } {
- t.Run(proto.Name(), func(t *testing.T) {
- // Test every combination of bound/unbound, broadcast/multicast/unicast
- // bound/destination address, and bound/not-bound to device.
- for _, bindTo := range []net.IP{
- nil, // Do not bind.
- net.IPv4zero,
- net.IPv4bcast,
- net.IPv4allsys,
- net.IPv6zero,
- subnetBroadcast,
- dut.Net.RemoteIPv4,
- dut.Net.RemoteIPv6,
- } {
- t.Run(fmt.Sprintf("bindTo=%s", bindTo), func(t *testing.T) {
- for _, sendTo := range []net.IP{
- net.IPv4bcast,
- net.IPv4allsys,
- subnetBroadcast,
- dut.Net.LocalIPv4,
- dut.Net.LocalIPv6,
- dut.Net.RemoteIPv4,
- dut.Net.RemoteIPv6,
- } {
- t.Run(fmt.Sprintf("sendTo=%s", sendTo), func(t *testing.T) {
- for _, bindToDevice := range []bool{true, false} {
- t.Run(fmt.Sprintf("bindToDevice=%t", bindToDevice), func(t *testing.T) {
- t.Run("Send", func(t *testing.T) {
- proto.Send(t, dut, bindTo, sendTo, bindToDevice)
- })
- t.Run("Receive", func(t *testing.T) {
- proto.Receive(t, dut, bindTo, sendTo, bindToDevice)
- })
- })
- }
- })
- }
- })
- }
- })
- }
-}
-
-type icmpV4TestEnv struct {
- socketFD int32
- ident uint16
- conn testbench.IPv4Conn
- layers testbench.Layers
-}
-
-type icmpV4Test struct{}
-
-func (test *icmpV4Test) setup(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP, bindToDevice bool) icmpV4TestEnv {
- t.Helper()
-
- // Tell the DUT to create a socket.
- var socketFD int32
- var ident uint16
-
- if bindTo != nil {
- socketFD, ident = dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_ICMP, bindTo)
- } else {
- // An unbound socket will auto-bind to INADDR_ANY.
- socketFD = dut.Socket(t, unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_ICMP)
- }
- t.Cleanup(func() {
- dut.Close(t, socketFD)
- })
-
- if bindToDevice {
- dut.SetSockOpt(t, socketFD, unix.SOL_SOCKET, unix.SO_BINDTODEVICE, []byte(dut.Net.RemoteDevName))
- }
-
- // Create a socket on the test runner.
- conn := dut.Net.NewIPv4Conn(t, testbench.IPv4{}, testbench.IPv4{})
- t.Cleanup(func() {
- conn.Close(t)
- })
-
- return icmpV4TestEnv{
- socketFD: socketFD,
- ident: ident,
- conn: conn,
- layers: testbench.Layers{
- expectedEthLayer(t, dut, socketFD, sendTo),
- &testbench.IPv4{
- DstAddr: testbench.Address(tcpip.Address(sendTo.To4())),
- },
- },
- }
-}
-
-var _ protocolTest = (*icmpV4Test)(nil)
-
-func (*icmpV4Test) Name() string { return "icmpv4" }
-
-func (test *icmpV4Test) Send(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP, bindToDevice bool) {
- if bindTo.To4() == nil || isBroadcastOrMulticast(dut, bindTo) {
- // ICMPv4 sockets cannot bind to IPv6, broadcast, or multicast
- // addresses.
- return
- }
-
- isV4 := sendTo.To4() != nil
-
- // TODO(gvisor.dev/issue/5681): Remove this case once ICMP sockets allow
- // sending to broadcast and multicast addresses.
- if (dut.Uname.IsGvisor() || dut.Uname.IsFuchsia()) && isV4 && isBroadcastOrMulticast(dut, sendTo) {
- // expectPacket cannot be false. In some cases the packet will send, but
- // with IPv4 destination incorrectly set to RemoteIPv4. It's all bad and
- // not worth the effort to create a special case when this occurs.
- t.Skip("TODO(gvisor.dev/issue/5681): Allow sending to broadcast and multicast addresses with ICMP sockets.")
- }
-
- expectPacket := isV4 && !sendTo.Equal(dut.Net.RemoteIPv4)
- switch {
- case bindTo.Equal(dut.Net.RemoteIPv4):
- // If we're explicitly bound to an interface's unicast address,
- // packets are always sent on that interface.
- case bindToDevice:
- // If we're explicitly bound to an interface, packets are always
- // sent on that interface.
- case !sendTo.Equal(net.IPv4bcast) && !sendTo.IsMulticast():
- // If we're not sending to limited broadcast or multicast, the route
- // table will be consulted and packets will be sent on the correct
- // interface.
- default:
- expectPacket = false
- }
-
- env := test.setup(t, dut, bindTo, sendTo, bindToDevice)
-
- for name, payload := range map[string][]byte{
- "empty": nil,
- "small": []byte("hello world"),
- "random1k": testbench.GenerateRandomPayload(t, maxICMPv4PayloadSize),
- } {
- t.Run(name, func(t *testing.T) {
- icmpLayer := &testbench.ICMPv4{
- Type: testbench.ICMPv4Type(header.ICMPv4Echo),
- Payload: payload,
- }
- bytes, err := icmpLayer.ToBytes()
- if err != nil {
- t.Fatalf("icmpLayer.ToBytes() = %s", err)
- }
- destSockaddr := unix.SockaddrInet4{}
- copy(destSockaddr.Addr[:], sendTo.To4())
-
- // Tell the DUT to send a packet out the ICMP socket.
- if got, want := dut.SendTo(t, env.socketFD, bytes, 0, &destSockaddr), len(bytes); int(got) != want {
- t.Fatalf("got dut.SendTo = %d, want %d", got, want)
- }
-
- // Verify the test runner received an ICMP packet with the correctly
- // set "ident".
- if env.ident != 0 {
- icmpLayer.Ident = &env.ident
- }
- want := append(env.layers, icmpLayer)
- if got, ok := env.conn.ListenForFrame(t, want, time.Second); !ok && expectPacket {
- t.Fatalf("did not receive expected frame matching %s\nGot frames: %s", want, got)
- } else if ok && !expectPacket {
- matchedFrame := got[len(got)-1]
- t.Fatalf("got unexpected frame matching %s\nGot frame: %s", want, matchedFrame)
- }
- })
- }
-}
-
-func (test *icmpV4Test) Receive(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP, bindToDevice bool) {
- if bindTo.To4() == nil || isBroadcastOrMulticast(dut, bindTo) {
- // ICMPv4 sockets cannot bind to IPv6, broadcast, or multicast
- // addresses.
- return
- }
-
- expectPacket := (bindTo.Equal(dut.Net.RemoteIPv4) || bindTo.Equal(net.IPv4zero)) && sendTo.Equal(dut.Net.RemoteIPv4)
-
- // TODO(gvisor.dev/issue/5763): Remove this if statement once gVisor
- // restricts ICMP sockets to receive only from unicast addresses.
- if (dut.Uname.IsGvisor() || dut.Uname.IsFuchsia()) && bindTo.Equal(net.IPv4zero) && isBroadcastOrMulticast(dut, sendTo) {
- expectPacket = true
- }
-
- env := test.setup(t, dut, bindTo, sendTo, bindToDevice)
-
- for name, payload := range map[string][]byte{
- "empty": nil,
- "small": []byte("hello world"),
- "random1k": testbench.GenerateRandomPayload(t, maxICMPv4PayloadSize),
- } {
- t.Run(name, func(t *testing.T) {
- icmpLayer := &testbench.ICMPv4{
- Type: testbench.ICMPv4Type(header.ICMPv4EchoReply),
- Payload: payload,
- }
- if env.ident != 0 {
- icmpLayer.Ident = &env.ident
- }
-
- // Send an ICMPv4 packet from the test runner to the DUT.
- frame := env.conn.CreateFrame(t, env.layers, icmpLayer)
- env.conn.SendFrame(t, frame)
-
- // Verify the behavior of the ICMP socket on the DUT.
- if expectPacket {
- payload, err := icmpLayer.ToBytes()
- if err != nil {
- t.Fatalf("icmpLayer.ToBytes() = %s", err)
- }
-
- // Receive one extra byte to assert the length of the
- // packet received in the case where the packet contains
- // more data than expected.
- len := int32(len(payload)) + 1
- got, want := dut.Recv(t, env.socketFD, len, 0), payload
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("received payload does not match sent payload, diff (-want, +got):\n%s", diff)
- }
- } else {
- // Expected receive error, set a short receive timeout.
- dut.SetSockOptTimeval(
- t,
- env.socketFD,
- unix.SOL_SOCKET,
- unix.SO_RCVTIMEO,
- &unix.Timeval{
- Sec: 1,
- Usec: 0,
- },
- )
- ret, recvPayload, errno := dut.RecvWithErrno(context.Background(), t, env.socketFD, maxICMPv4PayloadSize, 0)
- if errno != unix.EAGAIN || errno != unix.EWOULDBLOCK {
- t.Errorf("Recv got unexpected result, ret=%d, payload=%q, errno=%s", ret, recvPayload, errno)
- }
- }
- })
- }
-}
-
-type icmpV6TestEnv struct {
- socketFD int32
- ident uint16
- conn testbench.IPv6Conn
- layers testbench.Layers
-}
-
-// icmpV6Test and icmpV4Test look substantially similar at first look, but have
-// enough subtle differences in setup and test expectations to discourage
-// refactoring:
-// - Different IP layers
-// - Different testbench.Connections
-// - Different UNIX domain and proto arguments
-// - Different expectPacket and wantErrno for send and receive
-type icmpV6Test struct{}
-
-func (test *icmpV6Test) setup(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP, bindToDevice bool) icmpV6TestEnv {
- t.Helper()
-
- // Tell the DUT to create a socket.
- var socketFD int32
- var ident uint16
-
- if bindTo != nil {
- socketFD, ident = dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_ICMPV6, bindTo)
- } else {
- // An unbound socket will auto-bind to IN6ADDR_ANY_INIT.
- socketFD = dut.Socket(t, unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_ICMPV6)
- }
- t.Cleanup(func() {
- dut.Close(t, socketFD)
- })
-
- if bindToDevice {
- dut.SetSockOpt(t, socketFD, unix.SOL_SOCKET, unix.SO_BINDTODEVICE, []byte(dut.Net.RemoteDevName))
- }
-
- // Create a socket on the test runner.
- conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{})
- t.Cleanup(func() {
- conn.Close(t)
- })
-
- return icmpV6TestEnv{
- socketFD: socketFD,
- ident: ident,
- conn: conn,
- layers: testbench.Layers{
- expectedEthLayer(t, dut, socketFD, sendTo),
- &testbench.IPv6{
- DstAddr: testbench.Address(tcpip.Address(sendTo.To16())),
- },
- },
- }
-}
-
-var _ protocolTest = (*icmpV6Test)(nil)
-
-func (*icmpV6Test) Name() string { return "icmpv6" }
-
-func (test *icmpV6Test) Send(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP, bindToDevice bool) {
- if bindTo.To4() != nil || bindTo.IsMulticast() {
- // ICMPv6 sockets cannot bind to IPv4 or multicast addresses.
- return
- }
-
- expectPacket := sendTo.Equal(dut.Net.LocalIPv6)
- wantErrno := unix.Errno(0)
-
- if sendTo.To4() != nil {
- wantErrno = unix.EINVAL
- }
-
- // TODO(gvisor.dev/issue/5966): Remove this if statement once ICMPv6 sockets
- // return EINVAL after calling sendto with an IPv4 address.
- if (dut.Uname.IsGvisor() || dut.Uname.IsFuchsia()) && sendTo.To4() != nil {
- switch {
- case bindTo.Equal(dut.Net.RemoteIPv6):
- wantErrno = unix.ENETUNREACH
- case bindTo.Equal(net.IPv6zero) || bindTo == nil:
- wantErrno = unix.Errno(0)
- }
- }
-
- env := test.setup(t, dut, bindTo, sendTo, bindToDevice)
-
- for name, payload := range map[string][]byte{
- "empty": nil,
- "small": []byte("hello world"),
- "random1k": testbench.GenerateRandomPayload(t, maxICMPv6PayloadSize),
- } {
- t.Run(name, func(t *testing.T) {
- icmpLayer := &testbench.ICMPv6{
- Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest),
- Payload: payload,
- }
- bytes, err := icmpLayer.ToBytes()
- if err != nil {
- t.Fatalf("icmpLayer.ToBytes() = %s", err)
- }
- destSockaddr := unix.SockaddrInet6{
- ZoneId: dut.Net.RemoteDevID,
- }
- copy(destSockaddr.Addr[:], sendTo.To16())
-
- // Tell the DUT to send a packet out the ICMPv6 socket.
- gotRet, gotErrno := dut.SendToWithErrno(context.Background(), t, env.socketFD, bytes, 0, &destSockaddr)
-
- if gotErrno != wantErrno {
- t.Fatalf("got dut.SendToWithErrno(_, _, %d, _, _, %s) = (_, %s), want = (_, %s)", env.socketFD, sendTo, gotErrno, wantErrno)
- }
- if wantErrno != 0 {
- return
- }
- if got, want := int(gotRet), len(bytes); got != want {
- t.Fatalf("got dut.SendToWithErrno(_, _, %d, _, _, %s) = (%d, _), want = (%d, _)", env.socketFD, sendTo, got, want)
- }
-
- // Verify the test runner received an ICMPv6 packet with the
- // correctly set "ident".
- if env.ident != 0 {
- icmpLayer.Ident = &env.ident
- }
- want := append(env.layers, icmpLayer)
- if got, ok := env.conn.ListenForFrame(t, want, time.Second); !ok && expectPacket {
- t.Fatalf("did not receive expected frame matching %s\nGot frames: %s", want, got)
- } else if ok && !expectPacket {
- matchedFrame := got[len(got)-1]
- t.Fatalf("got unexpected frame matching %s\nGot frame: %s", want, matchedFrame)
- }
- })
- }
-}
-
-func (test *icmpV6Test) Receive(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP, bindToDevice bool) {
- if bindTo.To4() != nil || bindTo.IsMulticast() {
- // ICMPv6 sockets cannot bind to IPv4 or multicast addresses.
- return
- }
-
- expectPacket := true
- switch {
- case bindTo.Equal(dut.Net.RemoteIPv6) && sendTo.Equal(dut.Net.RemoteIPv6):
- case bindTo.Equal(net.IPv6zero) && sendTo.Equal(dut.Net.RemoteIPv6):
- case bindTo.Equal(net.IPv6zero) && sendTo.Equal(net.IPv6linklocalallnodes):
- default:
- expectPacket = false
- }
-
- // TODO(gvisor.dev/issue/5763): Remove this if statement once gVisor
- // restricts ICMP sockets to receive only from unicast addresses.
- if (dut.Uname.IsGvisor() || dut.Uname.IsFuchsia()) && bindTo.Equal(net.IPv6zero) && isBroadcastOrMulticast(dut, sendTo) {
- expectPacket = false
- }
-
- env := test.setup(t, dut, bindTo, sendTo, bindToDevice)
-
- for name, payload := range map[string][]byte{
- "empty": nil,
- "small": []byte("hello world"),
- "random1k": testbench.GenerateRandomPayload(t, maxICMPv6PayloadSize),
- } {
- t.Run(name, func(t *testing.T) {
- icmpLayer := &testbench.ICMPv6{
- Type: testbench.ICMPv6Type(header.ICMPv6EchoReply),
- Payload: payload,
- }
- if env.ident != 0 {
- icmpLayer.Ident = &env.ident
- }
-
- // Send an ICMPv6 packet from the test runner to the DUT.
- frame := env.conn.CreateFrame(t, env.layers, icmpLayer)
- env.conn.SendFrame(t, frame)
-
- // Verify the behavior of the ICMPv6 socket on the DUT.
- if expectPacket {
- payload, err := icmpLayer.ToBytes()
- if err != nil {
- t.Fatalf("icmpLayer.ToBytes() = %s", err)
- }
-
- // Receive one extra byte to assert the length of the
- // packet received in the case where the packet contains
- // more data than expected.
- len := int32(len(payload)) + 1
- got, want := dut.Recv(t, env.socketFD, len, 0), payload
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("received payload does not match sent payload, diff (-want, +got):\n%s", diff)
- }
- } else {
- // Expected receive error, set a short receive timeout.
- dut.SetSockOptTimeval(
- t,
- env.socketFD,
- unix.SOL_SOCKET,
- unix.SO_RCVTIMEO,
- &unix.Timeval{
- Sec: 1,
- Usec: 0,
- },
- )
- ret, recvPayload, errno := dut.RecvWithErrno(context.Background(), t, env.socketFD, maxICMPv6PayloadSize, 0)
- if errno != unix.EAGAIN || errno != unix.EWOULDBLOCK {
- t.Errorf("Recv got unexpected result, ret=%d, payload=%q, errno=%s", ret, recvPayload, errno)
- }
- }
- })
- }
-}
-
-type udpConn interface {
- SrcPort(*testing.T) uint16
- SendFrame(*testing.T, testbench.Layers, ...testbench.Layer)
- ListenForFrame(*testing.T, testbench.Layers, time.Duration) ([]testbench.Layers, bool)
- Close(*testing.T)
-}
-
-type udpTestEnv struct {
- socketFD int32
- conn udpConn
- layers testbench.Layers
-}
-
-type udpTest struct{}
-
-func (test *udpTest) setup(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP, bindToDevice bool) udpTestEnv {
- t.Helper()
-
- var (
- socketFD int32
- outgoingUDP, incomingUDP testbench.UDP
- )
-
- // Tell the DUT to create a socket.
- if bindTo != nil {
- var remotePort uint16
- socketFD, remotePort = dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, bindTo)
- outgoingUDP.DstPort = &remotePort
- incomingUDP.SrcPort = &remotePort
- } else {
- // An unbound socket will auto-bind to INADDR_ANY.
- socketFD = dut.Socket(t, unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
- }
- t.Cleanup(func() {
- dut.Close(t, socketFD)
- })
-
- if bindToDevice {
- dut.SetSockOpt(t, socketFD, unix.SOL_SOCKET, unix.SO_BINDTODEVICE, []byte(dut.Net.RemoteDevName))
- }
-
- // Create a socket on the test runner.
- var conn udpConn
- var ipLayer testbench.Layer
- if addr := sendTo.To4(); addr != nil {
- udpConn := dut.Net.NewUDPIPv4(t, outgoingUDP, incomingUDP)
- conn = &udpConn
- ipLayer = &testbench.IPv4{
- DstAddr: testbench.Address(tcpip.Address(addr)),
- }
- } else {
- udpConn := dut.Net.NewUDPIPv6(t, outgoingUDP, incomingUDP)
- conn = &udpConn
- ipLayer = &testbench.IPv6{
- DstAddr: testbench.Address(tcpip.Address(sendTo.To16())),
- }
- }
- t.Cleanup(func() {
- conn.Close(t)
- })
-
- return udpTestEnv{
- socketFD: socketFD,
- conn: conn,
- layers: testbench.Layers{
- expectedEthLayer(t, dut, socketFD, sendTo),
- ipLayer,
- &incomingUDP,
- },
- }
-}
-
-var _ protocolTest = (*udpTest)(nil)
-
-func (*udpTest) Name() string { return "udp" }
-
-func (test *udpTest) Send(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP, bindToDevice bool) {
- canSend := bindTo == nil || bindTo.Equal(net.IPv6zero) || sameIPVersion(sendTo, bindTo)
- expectPacket := canSend && !isRemoteAddr(dut, sendTo)
- switch {
- case bindTo.Equal(dut.Net.RemoteIPv4):
- // If we're explicitly bound to an interface's unicast address,
- // packets are always sent on that interface.
- case bindToDevice:
- // If we're explicitly bound to an interface, packets are always
- // sent on that interface.
- case !sendTo.Equal(net.IPv4bcast) && !sendTo.IsMulticast():
- // If we're not sending to limited broadcast, multicast, or local, the
- // route table will be consulted and packets will be sent on the correct
- // interface.
- default:
- expectPacket = false
- }
-
- wantErrno := unix.Errno(0)
- switch {
- case !canSend && bindTo.To4() != nil:
- wantErrno = unix.EAFNOSUPPORT
- case !canSend && bindTo.To4() == nil:
- wantErrno = unix.ENETUNREACH
- }
-
- // TODO(gvisor.dev/issue/5967): Remove this if statement once UDPv4 sockets
- // returns EAFNOSUPPORT after calling sendto with an IPv6 address.
- if dut.Uname.IsGvisor() && !canSend && bindTo.To4() != nil {
- wantErrno = unix.EINVAL
- }
-
- env := test.setup(t, dut, bindTo, sendTo, bindToDevice)
-
- for name, payload := range map[string][]byte{
- "empty": nil,
- "small": []byte("hello world"),
- "random1k": testbench.GenerateRandomPayload(t, maxUDPPayloadSize(bindTo)),
- } {
- t.Run(name, func(t *testing.T) {
- var destSockaddr unix.Sockaddr
- if sendTo4 := sendTo.To4(); sendTo4 != nil {
- addr := unix.SockaddrInet4{
- Port: int(env.conn.SrcPort(t)),
- }
- copy(addr.Addr[:], sendTo4)
- destSockaddr = &addr
- } else {
- addr := unix.SockaddrInet6{
- Port: int(env.conn.SrcPort(t)),
- ZoneId: dut.Net.RemoteDevID,
- }
- copy(addr.Addr[:], sendTo.To16())
- destSockaddr = &addr
- }
-
- // Tell the DUT to send a packet out the UDP socket.
- gotRet, gotErrno := dut.SendToWithErrno(context.Background(), t, env.socketFD, payload, 0, destSockaddr)
-
- if gotErrno != wantErrno {
- t.Fatalf("got dut.SendToWithErrno(_, _, %d, _, _, %s) = (_, %s), want = (_, %s)", env.socketFD, sendTo, gotErrno, wantErrno)
- }
- if wantErrno != 0 {
- return
- }
- if got, want := int(gotRet), len(payload); got != want {
- t.Fatalf("got dut.SendToWithErrno(_, _, %d, _, _, %s) = (%d, _), want = (%d, _)", env.socketFD, sendTo, got, want)
- }
-
- // Verify the test runner received a UDP packet with the
- // correct payload.
- want := append(env.layers, &testbench.Payload{
- Bytes: payload,
- })
- if got, ok := env.conn.ListenForFrame(t, want, time.Second); !ok && expectPacket {
- t.Fatalf("did not receive expected frame matching %s\nGot frames: %s", want, got)
- } else if ok && !expectPacket {
- matchedFrame := got[len(got)-1]
- t.Fatalf("got unexpected frame matching %s\nGot frame: %s", want, matchedFrame)
- }
- })
- }
-}
-
-func (test *udpTest) Receive(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP, bindToDevice bool) {
- subnetBroadcast := dut.Net.SubnetBroadcast()
-
- expectPacket := true
- switch {
- case bindTo.Equal(sendTo):
- case bindTo.Equal(net.IPv4zero) && sameIPVersion(bindTo, sendTo) && !sendTo.Equal(dut.Net.LocalIPv4):
- case bindTo.Equal(net.IPv6zero) && isBroadcast(dut, sendTo):
- case bindTo.Equal(net.IPv6zero) && isRemoteAddr(dut, sendTo):
- case bindTo.Equal(subnetBroadcast) && sendTo.Equal(subnetBroadcast):
- default:
- expectPacket = false
- }
-
- // TODO(gvisor.dev/issue/5956): Remove this if statement once gVisor
- // restricts ICMP sockets to receive only from unicast addresses.
- if (dut.Uname.IsGvisor() || dut.Uname.IsFuchsia()) && bindTo.Equal(net.IPv6zero) && sendTo.Equal(net.IPv4allsys) {
- expectPacket = true
- }
-
- env := test.setup(t, dut, bindTo, sendTo, bindToDevice)
- maxPayloadSize := maxUDPPayloadSize(bindTo)
-
- for name, payload := range map[string][]byte{
- "empty": nil,
- "small": []byte("hello world"),
- "random1k": testbench.GenerateRandomPayload(t, maxPayloadSize),
- } {
- t.Run(name, func(t *testing.T) {
- // Send a UDP packet from the test runner to the DUT.
- env.conn.SendFrame(t, env.layers, &testbench.Payload{Bytes: payload})
-
- // Verify the behavior of the ICMP socket on the DUT.
- if expectPacket {
- // Receive one extra byte to assert the length of the
- // packet received in the case where the packet contains
- // more data than expected.
- len := int32(len(payload)) + 1
- got, want := dut.Recv(t, env.socketFD, len, 0), payload
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("received payload does not match sent payload, diff (-want, +got):\n%s", diff)
- }
- } else {
- // Expected receive error, set a short receive timeout.
- dut.SetSockOptTimeval(
- t,
- env.socketFD,
- unix.SOL_SOCKET,
- unix.SO_RCVTIMEO,
- &unix.Timeval{
- Sec: 1,
- Usec: 0,
- },
- )
- ret, recvPayload, errno := dut.RecvWithErrno(context.Background(), t, env.socketFD, int32(maxPayloadSize), 0)
- if errno != unix.EAGAIN || errno != unix.EWOULDBLOCK {
- t.Errorf("Recv got unexpected result, ret=%d, payload=%q, errno=%s", ret, recvPayload, errno)
- }
- }
- })
- }
-}
-
-func isBroadcast(dut testbench.DUT, ip net.IP) bool {
- return ip.Equal(net.IPv4bcast) || ip.Equal(dut.Net.SubnetBroadcast())
-}
-
-func isBroadcastOrMulticast(dut testbench.DUT, ip net.IP) bool {
- return isBroadcast(dut, ip) || ip.IsMulticast()
-}
-
-func sameIPVersion(a, b net.IP) bool {
- return (a.To4() == nil) == (b.To4() == nil)
-}
-
-func isRemoteAddr(dut testbench.DUT, ip net.IP) bool {
- return ip.Equal(dut.Net.RemoteIPv4) || ip.Equal(dut.Net.RemoteIPv6)
-}
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 fdabcf1ad..000000000
--- a/test/packetimpact/tests/icmpv6_param_problem_test.go
+++ /dev/null
@@ -1,74 +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 (
- "flag"
- "testing"
- "time"
-
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(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)
- conn := dut.Net.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 := conn.CreateFrame(t, testbench.Layers{&ipv6}, &icmpv6)
- 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)
- }
-
- expectedICMPv6 := testbench.ICMPv6{
- Type: testbench.ICMPv6Type(header.ICMPv6ParamProblem),
- Payload: expectedPayload,
- // The problematic field is the NextHeader.
- Pointer: testbench.Uint32(header.IPv6NextHeaderOffset),
- }
-
- 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_fragment_reassembly_test.go b/test/packetimpact/tests/ipv4_fragment_reassembly_test.go
deleted file mode 100644
index 707f0f1f5..000000000
--- a/test/packetimpact/tests/ipv4_fragment_reassembly_test.go
+++ /dev/null
@@ -1,175 +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_fragment_reassembly_test
-
-import (
- "flag"
- "math/rand"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-type fragmentInfo struct {
- offset uint16
- size uint16
- more uint8
- id uint16
-}
-
-func TestIPv4FragmentReassembly(t *testing.T) {
- icmpv4ProtoNum := uint8(header.ICMPv4ProtocolNumber)
-
- tests := []struct {
- description string
- ipPayloadLen int
- fragments []fragmentInfo
- expectReply bool
- }{
- {
- description: "basic reassembly",
- ipPayloadLen: 3000,
- fragments: []fragmentInfo{
- {offset: 0, size: 1000, id: 5, more: header.IPv4FlagMoreFragments},
- {offset: 1000, size: 1000, id: 5, more: header.IPv4FlagMoreFragments},
- {offset: 2000, size: 1000, id: 5, more: 0},
- },
- expectReply: true,
- },
- {
- description: "out of order fragments",
- ipPayloadLen: 3000,
- fragments: []fragmentInfo{
- {offset: 2000, size: 1000, id: 6, more: 0},
- {offset: 0, size: 1000, id: 6, more: header.IPv4FlagMoreFragments},
- {offset: 1000, size: 1000, id: 6, more: header.IPv4FlagMoreFragments},
- },
- expectReply: true,
- },
- {
- description: "duplicated fragments",
- ipPayloadLen: 3000,
- fragments: []fragmentInfo{
- {offset: 0, size: 1000, id: 7, more: header.IPv4FlagMoreFragments},
- {offset: 1000, size: 1000, id: 7, more: header.IPv4FlagMoreFragments},
- {offset: 1000, size: 1000, id: 7, more: header.IPv4FlagMoreFragments},
- {offset: 2000, size: 1000, id: 7, more: 0},
- },
- expectReply: true,
- },
- {
- description: "fragment subset",
- ipPayloadLen: 3000,
- fragments: []fragmentInfo{
- {offset: 0, size: 1000, id: 8, more: header.IPv4FlagMoreFragments},
- {offset: 1000, size: 1000, id: 8, more: header.IPv4FlagMoreFragments},
- {offset: 512, size: 256, id: 8, more: header.IPv4FlagMoreFragments},
- {offset: 2000, size: 1000, id: 8, more: 0},
- },
- expectReply: true,
- },
- {
- description: "fragment overlap",
- ipPayloadLen: 3000,
- fragments: []fragmentInfo{
- {offset: 0, size: 1000, id: 9, more: header.IPv4FlagMoreFragments},
- {offset: 1512, size: 1000, id: 9, more: header.IPv4FlagMoreFragments},
- {offset: 1000, size: 1000, id: 9, more: header.IPv4FlagMoreFragments},
- {offset: 2000, size: 1000, id: 9, more: 0},
- },
- expectReply: false,
- },
- }
-
- for _, test := range tests {
- t.Run(test.description, func(t *testing.T) {
- dut := testbench.NewDUT(t)
- conn := dut.Net.NewIPv4Conn(t, testbench.IPv4{}, testbench.IPv4{})
- defer conn.Close(t)
-
- data := make([]byte, test.ipPayloadLen)
- icmp := header.ICMPv4(data[:header.ICMPv4MinimumSize])
- icmp.SetType(header.ICMPv4Echo)
- icmp.SetCode(header.ICMPv4UnusedCode)
- icmp.SetChecksum(0)
- icmp.SetSequence(0)
- icmp.SetIdent(0)
- originalPayload := data[header.ICMPv4MinimumSize:]
- if _, err := rand.Read(originalPayload); err != nil {
- t.Fatalf("rand.Read: %s", err)
- }
- cksum := header.ICMPv4Checksum(icmp, header.Checksum(originalPayload, 0 /* initial */))
- icmp.SetChecksum(cksum)
-
- for _, fragment := range test.fragments {
- conn.Send(t,
- testbench.IPv4{
- Protocol: &icmpv4ProtoNum,
- FragmentOffset: testbench.Uint16(fragment.offset),
- Flags: testbench.Uint8(fragment.more),
- ID: testbench.Uint16(fragment.id),
- },
- &testbench.Payload{
- Bytes: data[fragment.offset:][:fragment.size],
- })
- }
-
- var bytesReceived int
- reassembledPayload := make([]byte, test.ipPayloadLen)
- // We are sending a packet fragmented into smaller parts but the
- // response may also be large enough to require fragmentation.
- // Therefore we only look for payload for an IPv4 packet not ICMP.
- for {
- incomingFrame, err := conn.ExpectFrame(t, testbench.Layers{
- &testbench.Ether{},
- &testbench.IPv4{},
- }, time.Second)
- if err != nil {
- // Either an unexpected frame was received, or none at all.
- if test.expectReply && bytesReceived < test.ipPayloadLen {
- t.Fatalf("received %d bytes out of %d, then conn.ExpectFrame(_, _, time.Second) failed with %s", bytesReceived, test.ipPayloadLen, err)
- }
- break
- }
- if !test.expectReply {
- t.Fatalf("unexpected reply received:\n%s", incomingFrame)
- }
- // We only asked for Ethernet and IPv4 so the rest should be payload.
- ipPayload, err := incomingFrame[2 /* Payload */].ToBytes()
- if err != nil {
- t.Fatalf("failed to parse payload: incomingPacket[2].ToBytes() = (_, %s)", err)
- }
- offset := *incomingFrame[1 /* IPv4 */].(*testbench.IPv4).FragmentOffset
- if copied := copy(reassembledPayload[offset:], ipPayload); copied != len(ipPayload) {
- t.Fatalf("wrong number of bytes copied into reassembledPayload: got = %d, want = %d", copied, len(ipPayload))
- }
- bytesReceived += len(ipPayload)
- }
-
- if test.expectReply {
- if diff := cmp.Diff(originalPayload, reassembledPayload[header.ICMPv4MinimumSize:]); diff != "" {
- t.Fatalf("reassembledPayload mismatch (-want +got):\n%s", diff)
- }
- }
- })
- }
-}
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 2b69ceecb..000000000
--- a/test/packetimpact/tests/ipv4_id_uniqueness_test.go
+++ /dev/null
@@ -1,121 +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.Initialize(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)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFD)
-
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- 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)
- // Fuchsia will return ENOPROTOPT errno.
- if ret == -1 && errno != unix.ENOPROTOOPT {
- 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.TCPFlags(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_icmp_error_test.go b/test/packetimpact/tests/ipv6_fragment_icmp_error_test.go
deleted file mode 100644
index 4034a128e..000000000
--- a/test/packetimpact/tests/ipv6_fragment_icmp_error_test.go
+++ /dev/null
@@ -1,356 +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_icmp_error_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-const (
- data = "IPV6_PROTOCOL_TESTER_FOR_FRAGMENT"
- fragmentID = 1
- reassemblyTimeout = ipv6.ReassembleTimeout + 5*time.Second
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-func fragmentedICMPEchoRequest(t *testing.T, n *testbench.DUTTestNet, conn *testbench.IPv6Conn, firstPayloadLength uint16, payload []byte, secondFragmentOffset uint16) ([]testbench.Layers, [][]byte) {
- t.Helper()
-
- icmpv6Header := header.ICMPv6(make([]byte, header.ICMPv6EchoMinimumSize))
- icmpv6Header.SetType(header.ICMPv6EchoRequest)
- icmpv6Header.SetCode(header.ICMPv6UnusedCode)
- icmpv6Header.SetIdent(0)
- icmpv6Header.SetSequence(0)
- cksum := header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
- Header: icmpv6Header,
- Src: tcpip.Address(n.LocalIPv6),
- Dst: tcpip.Address(n.RemoteIPv6),
- PayloadCsum: header.Checksum(payload, 0 /* initial */),
- PayloadLen: len(payload),
- })
- icmpv6Header.SetChecksum(cksum)
- icmpv6Bytes := append([]byte(icmpv6Header), payload...)
-
- icmpv6ProtoNum := header.IPv6ExtensionHeaderIdentifier(header.ICMPv6ProtocolNumber)
-
- firstFragment := conn.CreateFrame(t, testbench.Layers{&testbench.IPv6{}},
- &testbench.IPv6FragmentExtHdr{
- NextHeader: &icmpv6ProtoNum,
- FragmentOffset: testbench.Uint16(0),
- MoreFragments: testbench.Bool(true),
- Identification: testbench.Uint32(fragmentID),
- },
- &testbench.Payload{
- Bytes: icmpv6Bytes[:header.ICMPv6PayloadOffset+firstPayloadLength],
- },
- )
- firstIPv6 := firstFragment[1:]
- firstIPv6Bytes, err := firstIPv6.ToBytes()
- if err != nil {
- t.Fatalf("failed to convert first %s to bytes: %s", firstIPv6, err)
- }
-
- secondFragment := conn.CreateFrame(t, testbench.Layers{&testbench.IPv6{}},
- &testbench.IPv6FragmentExtHdr{
- NextHeader: &icmpv6ProtoNum,
- FragmentOffset: testbench.Uint16(secondFragmentOffset),
- MoreFragments: testbench.Bool(false),
- Identification: testbench.Uint32(fragmentID),
- },
- &testbench.Payload{
- Bytes: icmpv6Bytes[header.ICMPv6PayloadOffset+firstPayloadLength:],
- },
- )
- secondIPv6 := secondFragment[1:]
- secondIPv6Bytes, err := secondIPv6.ToBytes()
- if err != nil {
- t.Fatalf("failed to convert second %s to bytes: %s", secondIPv6, err)
- }
-
- return []testbench.Layers{firstFragment, secondFragment}, [][]byte{firstIPv6Bytes, secondIPv6Bytes}
-}
-
-func TestIPv6ICMPEchoRequestFragmentReassembly(t *testing.T) {
- tests := []struct {
- name string
- firstPayloadLength uint16
- payload []byte
- secondFragmentOffset uint16
- sendFrameOrder []int
- }{
- {
- name: "reassemble two fragments",
- firstPayloadLength: 8,
- payload: []byte(data)[:20],
- secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 8) / 8,
- sendFrameOrder: []int{1, 2},
- },
- {
- name: "reassemble two fragments in reverse order",
- firstPayloadLength: 8,
- payload: []byte(data)[:20],
- secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 8) / 8,
- sendFrameOrder: []int{2, 1},
- },
- }
-
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- t.Parallel()
- dut := testbench.NewDUT(t)
- conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{})
- defer conn.Close(t)
-
- fragments, _ := fragmentedICMPEchoRequest(t, dut.Net, &conn, test.firstPayloadLength, test.payload, test.secondFragmentOffset)
-
- for _, i := range test.sendFrameOrder {
- conn.SendFrame(t, fragments[i-1])
- }
-
- gotEchoReply, err := conn.ExpectFrame(t, testbench.Layers{
- &testbench.Ether{},
- &testbench.IPv6{},
- &testbench.ICMPv6{
- Type: testbench.ICMPv6Type(header.ICMPv6EchoReply),
- Code: testbench.ICMPv6Code(header.ICMPv6UnusedCode),
- },
- }, time.Second)
- if err != nil {
- t.Fatalf("didn't receive an ICMPv6 Echo Reply: %s", err)
- }
- gotPayload, err := gotEchoReply[len(gotEchoReply)-1].ToBytes()
- if err != nil {
- t.Fatalf("failed to convert ICMPv6 to bytes: %s", err)
- }
- icmpPayload := gotPayload[header.ICMPv6EchoMinimumSize:]
- wantPayload := test.payload
- if diff := cmp.Diff(wantPayload, icmpPayload); diff != "" {
- t.Fatalf("payload mismatch (-want +got):\n%s", diff)
- }
- })
- }
-}
-
-func TestIPv6FragmentReassemblyTimeout(t *testing.T) {
- type icmpFramePattern struct {
- typ header.ICMPv6Type
- code header.ICMPv6Code
- }
-
- type icmpReassemblyTimeoutDetail struct {
- payloadFragment int // 1: first fragment, 2: second fragnemt.
- }
-
- tests := []struct {
- name string
- firstPayloadLength uint16
- payload []byte
- secondFragmentOffset uint16
- sendFrameOrder []int
- replyFilter icmpFramePattern
- expectErrorReply bool
- expectICMPReassemblyTimeout icmpReassemblyTimeoutDetail
- }{
- {
- name: "reassembly timeout (first fragment only)",
- firstPayloadLength: 8,
- payload: []byte(data)[:20],
- secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 8) / 8,
- sendFrameOrder: []int{1},
- replyFilter: icmpFramePattern{
- typ: header.ICMPv6TimeExceeded,
- code: header.ICMPv6ReassemblyTimeout,
- },
- expectErrorReply: true,
- expectICMPReassemblyTimeout: icmpReassemblyTimeoutDetail{
- payloadFragment: 1,
- },
- },
- {
- name: "reassembly timeout (second fragment only)",
- firstPayloadLength: 8,
- payload: []byte(data)[:20],
- secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 8) / 8,
- sendFrameOrder: []int{2},
- replyFilter: icmpFramePattern{
- typ: header.ICMPv6TimeExceeded,
- code: header.ICMPv6ReassemblyTimeout,
- },
- expectErrorReply: false,
- },
- {
- name: "reassembly timeout (two fragments with a gap)",
- firstPayloadLength: 8,
- payload: []byte(data)[:20],
- secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 16) / 8,
- sendFrameOrder: []int{1, 2},
- replyFilter: icmpFramePattern{
- typ: header.ICMPv6TimeExceeded,
- code: header.ICMPv6ReassemblyTimeout,
- },
- expectErrorReply: true,
- expectICMPReassemblyTimeout: icmpReassemblyTimeoutDetail{
- payloadFragment: 1,
- },
- },
- }
-
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- t.Parallel()
- dut := testbench.NewDUT(t)
- conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{})
- defer conn.Close(t)
-
- fragments, ipv6Bytes := fragmentedICMPEchoRequest(t, dut.Net, &conn, test.firstPayloadLength, test.payload, test.secondFragmentOffset)
-
- for _, i := range test.sendFrameOrder {
- conn.SendFrame(t, fragments[i-1])
- }
-
- gotErrorMessage, err := conn.ExpectFrame(t, testbench.Layers{
- &testbench.Ether{},
- &testbench.IPv6{},
- &testbench.ICMPv6{
- Type: testbench.ICMPv6Type(test.replyFilter.typ),
- Code: testbench.ICMPv6Code(test.replyFilter.code),
- },
- }, reassemblyTimeout)
- if !test.expectErrorReply {
- if err == nil {
- t.Fatalf("shouldn't receive an ICMPv6 Error Message with type=%d and code=%d", test.replyFilter.typ, test.replyFilter.code)
- }
- return
- }
- if err != nil {
- t.Fatalf("didn't receive an ICMPv6 Error Message with type=%d and code=%d: err", test.replyFilter.typ, test.replyFilter.code, err)
- }
- gotPayload, err := gotErrorMessage[len(gotErrorMessage)-1].ToBytes()
- if err != nil {
- t.Fatalf("failed to convert ICMPv6 to bytes: %s", err)
- }
- icmpPayload := gotPayload[header.ICMPv6ErrorHeaderSize:]
- wantPayload := ipv6Bytes[test.expectICMPReassemblyTimeout.payloadFragment-1]
- if diff := cmp.Diff(wantPayload, icmpPayload); diff != "" {
- t.Fatalf("payload mismatch (-want +got):\n%s", diff)
- }
- })
- }
-}
-
-func TestIPv6FragmentParamProblem(t *testing.T) {
- type icmpFramePattern struct {
- typ header.ICMPv6Type
- code header.ICMPv6Code
- }
-
- type icmpParamProblemDetail struct {
- pointer uint32
- payloadFragment int // 1: first fragment, 2: second fragnemt.
- }
-
- tests := []struct {
- name string
- firstPayloadLength uint16
- payload []byte
- secondFragmentOffset uint16
- sendFrameOrder []int
- replyFilter icmpFramePattern
- expectICMPParamProblem icmpParamProblemDetail
- }{
- {
- name: "payload size not a multiple of 8",
- firstPayloadLength: 9,
- payload: []byte(data)[:20],
- secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 8) / 8,
- sendFrameOrder: []int{1},
- replyFilter: icmpFramePattern{
- typ: header.ICMPv6ParamProblem,
- code: header.ICMPv6ErroneousHeader,
- },
- expectICMPParamProblem: icmpParamProblemDetail{
- pointer: 4,
- payloadFragment: 1,
- },
- },
- {
- name: "payload length error",
- firstPayloadLength: 16,
- payload: []byte(data)[:33],
- secondFragmentOffset: 65520 / 8,
- sendFrameOrder: []int{1, 2},
- replyFilter: icmpFramePattern{
- typ: header.ICMPv6ParamProblem,
- code: header.ICMPv6ErroneousHeader,
- },
- expectICMPParamProblem: icmpParamProblemDetail{
- pointer: 42,
- payloadFragment: 2,
- },
- },
- }
-
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- t.Parallel()
- dut := testbench.NewDUT(t)
- conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{})
- defer conn.Close(t)
-
- fragments, ipv6Bytes := fragmentedICMPEchoRequest(t, dut.Net, &conn, test.firstPayloadLength, test.payload, test.secondFragmentOffset)
-
- for _, i := range test.sendFrameOrder {
- conn.SendFrame(t, fragments[i-1])
- }
-
- gotErrorMessage, err := conn.ExpectFrame(t, testbench.Layers{
- &testbench.Ether{},
- &testbench.IPv6{},
- &testbench.ICMPv6{
- Type: testbench.ICMPv6Type(test.replyFilter.typ),
- Code: testbench.ICMPv6Code(test.replyFilter.code),
- },
- }, time.Second)
- if err != nil {
- t.Fatalf("didn't receive an ICMPv6 Error Message with type=%d and code=%d: err", test.replyFilter.typ, test.replyFilter.code, err)
- }
- gotPayload, err := gotErrorMessage[len(gotErrorMessage)-1].ToBytes()
- if err != nil {
- t.Fatalf("failed to convert ICMPv6 to bytes: %s", err)
- }
- gotPointer := header.ICMPv6(gotPayload).TypeSpecific()
- wantPointer := test.expectICMPParamProblem.pointer
- if gotPointer != wantPointer {
- t.Fatalf("got pointer = %d, want = %d", gotPointer, wantPointer)
- }
- icmpPayload := gotPayload[header.ICMPv6ErrorHeaderSize:]
- wantPayload := ipv6Bytes[test.expectICMPParamProblem.payloadFragment-1]
- if diff := cmp.Diff(wantPayload, icmpPayload); diff != "" {
- t.Fatalf("payload mismatch (-want +got):\n%s", diff)
- }
- })
- }
-}
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 db6195dc9..000000000
--- a/test/packetimpact/tests/ipv6_fragment_reassembly_test.go
+++ /dev/null
@@ -1,182 +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 (
- "flag"
- "math/rand"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-type fragmentInfo struct {
- offset uint16
- size uint16
- more bool
- id uint32
-}
-
-func TestIPv6FragmentReassembly(t *testing.T) {
- icmpv6ProtoNum := header.IPv6ExtensionHeaderIdentifier(header.ICMPv6ProtocolNumber)
-
- tests := []struct {
- description string
- ipPayloadLen int
- fragments []fragmentInfo
- expectReply bool
- }{
- {
- description: "basic reassembly",
- ipPayloadLen: 3000,
- fragments: []fragmentInfo{
- {offset: 0, size: 1000, id: 100, more: true},
- {offset: 1000, size: 1000, id: 100, more: true},
- {offset: 2000, size: 1000, id: 100, more: false},
- },
- expectReply: true,
- },
- {
- description: "out of order fragments",
- ipPayloadLen: 3000,
- fragments: []fragmentInfo{
- {offset: 0, size: 1000, id: 101, more: true},
- {offset: 2000, size: 1000, id: 101, more: false},
- {offset: 1000, size: 1000, id: 101, more: true},
- },
- expectReply: true,
- },
- {
- description: "duplicated fragments",
- ipPayloadLen: 3000,
- fragments: []fragmentInfo{
- {offset: 0, size: 1000, id: 102, more: true},
- {offset: 1000, size: 1000, id: 102, more: true},
- {offset: 1000, size: 1000, id: 102, more: true},
- {offset: 2000, size: 1000, id: 102, more: false},
- },
- expectReply: true,
- },
- {
- description: "fragment subset",
- ipPayloadLen: 3000,
- fragments: []fragmentInfo{
- {offset: 0, size: 1000, id: 103, more: true},
- {offset: 1000, size: 1000, id: 103, more: true},
- {offset: 512, size: 256, id: 103, more: true},
- {offset: 2000, size: 1000, id: 103, more: false},
- },
- expectReply: true,
- },
- {
- description: "fragment overlap",
- ipPayloadLen: 3000,
- fragments: []fragmentInfo{
- {offset: 0, size: 1000, id: 104, more: true},
- {offset: 1512, size: 1000, id: 104, more: true},
- {offset: 1000, size: 1000, id: 104, more: true},
- {offset: 2000, size: 1000, id: 104, more: false},
- },
- expectReply: false,
- },
- }
-
- for _, test := range tests {
- t.Run(test.description, func(t *testing.T) {
- dut := testbench.NewDUT(t)
- conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{})
- defer conn.Close(t)
-
- lIP := tcpip.Address(dut.Net.LocalIPv6)
- rIP := tcpip.Address(dut.Net.RemoteIPv6)
-
- data := make([]byte, test.ipPayloadLen)
- icmp := header.ICMPv6(data[:header.ICMPv6HeaderSize])
- icmp.SetType(header.ICMPv6EchoRequest)
- icmp.SetCode(header.ICMPv6UnusedCode)
- icmp.SetChecksum(0)
- originalPayload := data[header.ICMPv6HeaderSize:]
- if _, err := rand.Read(originalPayload); err != nil {
- t.Fatalf("rand.Read: %s", err)
- }
-
- cksum := header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
- Header: icmp,
- Src: lIP,
- Dst: rIP,
- PayloadCsum: header.Checksum(originalPayload, 0 /* initial */),
- PayloadLen: len(originalPayload),
- })
- icmp.SetChecksum(cksum)
-
- for _, fragment := range test.fragments {
- conn.Send(t, testbench.IPv6{},
- &testbench.IPv6FragmentExtHdr{
- NextHeader: &icmpv6ProtoNum,
- FragmentOffset: testbench.Uint16(fragment.offset / header.IPv6FragmentExtHdrFragmentOffsetBytesPerUnit),
- MoreFragments: testbench.Bool(fragment.more),
- Identification: testbench.Uint32(fragment.id),
- },
- &testbench.Payload{
- Bytes: data[fragment.offset:][:fragment.size],
- })
- }
-
- var bytesReceived int
- reassembledPayload := make([]byte, test.ipPayloadLen)
- for {
- incomingFrame, err := conn.ExpectFrame(t, testbench.Layers{
- &testbench.Ether{},
- &testbench.IPv6{},
- &testbench.IPv6FragmentExtHdr{},
- }, time.Second)
- if err != nil {
- // Either an unexpected frame was received, or none at all.
- if test.expectReply && bytesReceived < test.ipPayloadLen {
- t.Fatalf("received %d bytes out of %d, then conn.ExpectFrame(_, _, time.Second) failed with %s", bytesReceived, test.ipPayloadLen, err)
- }
- break
- }
- if !test.expectReply {
- t.Fatalf("unexpected reply received:\n%s", incomingFrame)
- }
- ipPayload, err := incomingFrame[3 /* Payload */].ToBytes()
- if err != nil {
- t.Fatalf("failed to parse ICMPv6 header: incomingPacket[3].ToBytes() = (_, %s)", err)
- }
- offset := *incomingFrame[2 /* IPv6FragmentExtHdr */].(*testbench.IPv6FragmentExtHdr).FragmentOffset
- offset *= header.IPv6FragmentExtHdrFragmentOffsetBytesPerUnit
- if copied := copy(reassembledPayload[offset:], ipPayload); copied != len(ipPayload) {
- t.Fatalf("wrong number of bytes copied into reassembledPayload: got = %d, want = %d", copied, len(ipPayload))
- }
- bytesReceived += len(ipPayload)
- }
-
- if test.expectReply {
- if diff := cmp.Diff(originalPayload, reassembledPayload[header.ICMPv6HeaderSize:]); diff != "" {
- t.Fatalf("reassembledPayload mismatch (-want +got):\n%s", diff)
- }
- }
- })
- }
-}
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 d762c43a7..000000000
--- a/test/packetimpact/tests/ipv6_unknown_options_action_test.go
+++ /dev/null
@@ -1,183 +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 (
- "flag"
- "net"
- "testing"
- "time"
-
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(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)
- conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{})
- defer conn.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:]
- icmpv6Payload, err := ipv6Sent.ToBytes()
- if err != nil {
- t.Fatalf("failed to serialize the outgoing packet: %s", err)
- }
- gotICMPv6, err := conn.ExpectFrame(t, testbench.Layers{
- &testbench.Ether{},
- &testbench.IPv6{},
- &testbench.ICMPv6{
- Type: testbench.ICMPv6Type(header.ICMPv6ParamProblem),
- Code: testbench.ICMPv6Code(header.ICMPv6UnknownOption),
- // 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).
- Pointer: testbench.Uint32(header.IPv6MinimumSize + 2),
- 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_connect_icmp_error_test.go b/test/packetimpact/tests/tcp_connect_icmp_error_test.go
deleted file mode 100644
index 15d603328..000000000
--- a/test/packetimpact/tests/tcp_connect_icmp_error_test.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2021 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package tcp_connect_icmp_error_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-func sendICMPError(t *testing.T, conn *testbench.TCPIPv4, tcp *testbench.TCP) {
- t.Helper()
-
- icmpPayload := testbench.Layers{tcp.Prev(), tcp}
- bytes, err := icmpPayload.ToBytes()
- if err != nil {
- t.Fatalf("got icmpPayload.ToBytes() = (_, %s), want = (_, nil)", err)
- }
-
- layers := conn.CreateFrame(t, nil)
- layers[len(layers)-1] = &testbench.ICMPv4{
- Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable),
- Code: testbench.ICMPv4Code(header.ICMPv4HostUnreachable),
- Payload: bytes,
- }
- conn.SendFrameStateless(t, layers)
-}
-
-// TestTCPConnectICMPError tests for the handshake to fail and the socket state
-// cleaned up on receiving an ICMP error.
-func TestTCPConnectICMPError(t *testing.T) {
- dut := testbench.NewDUT(t)
-
- clientFD, clientPort := dut.CreateBoundSocket(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
- port := uint16(9001)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{SrcPort: &port, DstPort: &clientPort}, testbench.TCP{SrcPort: &clientPort, DstPort: &port})
- defer conn.Close(t)
- sa := unix.SockaddrInet4{Port: int(port)}
- copy(sa.Addr[:], dut.Net.LocalIPv4)
- // Bring the dut to SYN-SENT state with a non-blocking connect.
- dut.Connect(t, clientFD, &sa)
- tcp, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second)
- if err != nil {
- t.Fatalf("expected SYN, %s", err)
- }
-
- // Continuously try to read the ICMP error in an attempt to trigger a race
- // condition.
- start := make(chan struct{})
- done := make(chan struct{})
- go func() {
- defer close(done)
-
- close(start)
- for {
- select {
- case <-done:
- return
- default:
- }
- const want = unix.EHOSTUNREACH
- switch got := unix.Errno(dut.GetSockOptInt(t, clientFD, unix.SOL_SOCKET, unix.SO_ERROR)); got {
- case unix.Errno(0):
- continue
- case want:
- return
- default:
- t.Fatalf("got SO_ERROR = %s, want %s", got, want)
- }
-
- }
- }()
-
- <-start
- sendICMPError(t, &conn, tcp)
-
- dut.PollOne(t, clientFD, unix.POLLHUP, time.Second)
- <-done
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- // The DUT should reply with RST to our ACK as the state should have
- // transitioned to CLOSED because of handshake error.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, time.Second); err != nil {
- t.Fatalf("expected RST, %s", err)
- }
-}
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 1db3c9883..000000000
--- a/test/packetimpact/tests/tcp_cork_mss_test.go
+++ /dev/null
@@ -1,83 +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.Initialize(flag.CommandLine)
-}
-
-// TestTCPCorkMSS tests for segment coalesce and split as per MSS.
-func TestTCPCorkMSS(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- 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.TCPFlags(header.TCPFlagAck)}, &expectedPayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- // Expect the coalesced segment to be split and transmitted.
- expectedPayload = testbench.Payload{Bytes: expectedData[mss:]}
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, &expectedPayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-
- // Check for segments to *not* be held up because of TCP_CORK when
- // the current send window is less than MSS.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), WindowSize: testbench.Uint16(uint16(2 * len(sampleData)))})
- dut.Send(t, acceptFD, sampleData, 0)
- dut.Send(t, acceptFD, sampleData, 0)
- expectedPayload = testbench.Payload{Bytes: append(sampleData, sampleData...)}
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, &expectedPayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
-}
diff --git a/test/packetimpact/tests/tcp_fin_retransmission_test.go b/test/packetimpact/tests/tcp_fin_retransmission_test.go
deleted file mode 100644
index 500f7a783..000000000
--- a/test/packetimpact/tests/tcp_fin_retransmission_test.go
+++ /dev/null
@@ -1,87 +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_fin_retransmission_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-// TestTCPClosingFinRetransmission tests that TCP implementation should retransmit
-// FIN segment in CLOSING state.
-func TestTCPClosingFinRetransmission(t *testing.T) {
- for _, tt := range []struct {
- description string
- flags header.TCPFlags
- }{
- {"CLOSING", header.TCPFlagAck | header.TCPFlagFin},
- {"FIN_WAIT_1", header.TCPFlagAck},
- } {
- t.Run(tt.description, func(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
- conn.Connect(t)
- acceptFD, _ := dut.Accept(t, listenFD)
- defer dut.Close(t, acceptFD)
-
- // Give a chance for the dut to estimate RTO with RTT from the DATA-ACK.
- // TODO(gvisor.dev/issue/2685) Estimate RTO during handshake, after which
- // we can skip the next block of code.
- sampleData := []byte("Sample Data")
- if got, want := dut.Send(t, acceptFD, sampleData, 0), len(sampleData); int(got) != want {
- t.Fatalf("got dut.Send(t, %d, %s, 0) = %d, want %d", acceptFD, sampleData, got, want)
- }
- if _, err := conn.ExpectData(t, &testbench.TCP{}, &testbench.Payload{Bytes: sampleData}, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
-
- dut.Shutdown(t, acceptFD, unix.SHUT_WR)
-
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected FINACK from DUT, but got none: %s", err)
- }
-
- // Do not ack the FIN from DUT so that we can test for retransmission.
- seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1)
- conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(tt.flags)})
-
- if tt.flags&header.TCPFlagFin != 0 {
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected an ACK to our FIN, but got none: %s", err)
- }
- }
-
- if _, err := conn.Expect(t, testbench.TCP{
- SeqNum: seqNumForTheirFIN,
- Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck),
- }, time.Second); err != nil {
- t.Errorf("expected retransmission of FIN from the DUT: %s", err)
- }
- })
- }
-}
diff --git a/test/packetimpact/tests/tcp_handshake_window_size_test.go b/test/packetimpact/tests/tcp_handshake_window_size_test.go
deleted file mode 100644
index 668e0275c..000000000
--- a/test/packetimpact/tests/tcp_handshake_window_size_test.go
+++ /dev/null
@@ -1,65 +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.Initialize(flag.CommandLine)
-}
-
-// TestTCPHandshakeWindowSize tests if the stack is honoring the window size
-// communicated during handshake.
-func TestTCPHandshakeWindowSize(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- // Start handshake with zero window size.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn), WindowSize: testbench.Uint16(uint16(0))})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected SYN-ACK: %s", err)
- }
- // Update the advertised window size to a non-zero value with the ACK that
- // 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.TCPFlags(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.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-}
diff --git a/test/packetimpact/tests/tcp_info_test.go b/test/packetimpact/tests/tcp_info_test.go
deleted file mode 100644
index 5410cc368..000000000
--- a/test/packetimpact/tests/tcp_info_test.go
+++ /dev/null
@@ -1,102 +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_info_test
-
-import (
- "flag"
- "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.Initialize(flag.CommandLine)
-}
-
-func TestTCPInfo(t *testing.T) {
- // Create a socket, listen, TCP connect, and accept.
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFD)
-
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
- conn.Connect(t)
-
- acceptFD, _ := dut.Accept(t, listenFD)
- defer dut.Close(t, acceptFD)
-
- // Send and receive sample data.
- 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 a packet with payload %s: %s", samplePayload, err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
-
- info := dut.GetSockOptTCPInfo(t, acceptFD)
- if got, want := uint32(info.State), linux.TCP_ESTABLISHED; got != want {
- t.Fatalf("got %d want %d", got, want)
- }
- if info.RTT == 0 {
- t.Errorf("got RTT=0, want nonzero")
- }
- if info.RTTVar == 0 {
- t.Errorf("got RTTVar=0, want nonzero")
- }
- if info.RTO == 0 {
- t.Errorf("got RTO=0, want nonzero")
- }
- if info.ReordSeen != 0 {
- t.Errorf("expected the connection to not have any reordering, got: %d want: 0", info.ReordSeen)
- }
- if info.SndCwnd == 0 {
- t.Errorf("expected send congestion window to be greater than zero")
- }
- if info.CaState != linux.TCP_CA_Open {
- t.Errorf("expected the connection to be in open state, got: %d want: %d", info.CaState, linux.TCP_CA_Open)
- }
-
- if t.Failed() {
- t.FailNow()
- }
-
- // Check the congestion control state and send congestion window after
- // retransmission timeout.
- seq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)))
- dut.Send(t, acceptFD, sampleData, 0)
- if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected a packet with payload %s: %s", samplePayload, err)
- }
-
- // Given a generous retransmission timeout.
- timeout := time.Duration(info.RTO) * 2 * time.Microsecond
- if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: seq}, samplePayload, timeout); err != nil {
- t.Fatalf("expected a packet with payload %s: %s", samplePayload, err)
- }
-
- info = dut.GetSockOptTCPInfo(t, acceptFD)
- if info.CaState != linux.TCP_CA_Loss {
- t.Errorf("expected the connection to be in loss recovery, got: %d want: %d", info.CaState, linux.TCP_CA_Loss)
- }
- if info.SndCwnd != 1 {
- t.Errorf("expected send congestion window to be 1, got: %d", info.SndCwnd)
- }
-}
diff --git a/test/packetimpact/tests/tcp_linger_test.go b/test/packetimpact/tests/tcp_linger_test.go
deleted file mode 100644
index 46b5ca5d8..000000000
--- a/test/packetimpact/tests/tcp_linger_test.go
+++ /dev/null
@@ -1,265 +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"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-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 := dut.Net.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)
-}
-
-// 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.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected RST-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(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.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(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)
-
- start := time.Now()
- dut.CloseWithErrno(context.Background(), t, acceptFD)
- elapsed := time.Since(start)
-
- expectedMaximum := time.Second
- if tt.lingerOn {
- expectedMaximum += lingerDuration
- if elapsed < lingerDuration {
- t.Errorf("expected close to take at least %s, but took %s", lingerDuration, elapsed)
- }
- }
- if elapsed >= expectedMaximum {
- t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
- }
-
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(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)
-
- start := time.Now()
- dut.CloseWithErrno(context.Background(), t, acceptFD)
- elapsed := time.Since(start)
-
- expectedMaximum := time.Second
- if tt.lingerOn {
- expectedMaximum += lingerDuration
- if elapsed < lingerDuration {
- t.Errorf("expected close to take at least %s, but took %s", lingerDuration, elapsed)
- }
- }
- if elapsed >= expectedMaximum {
- t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
- }
-
- 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.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(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, unix.SHUT_RDWR)
- dut.Close(t, acceptFD)
-
- // Shutdown will send FIN-ACK with read/write option.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
- }
-
- // If the linger timeout is set to zero, the DUT should send a RST.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected RST-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(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, unix.SHUT_RDWR)
-
- start := time.Now()
- dut.CloseWithErrno(context.Background(), t, acceptFD)
- elapsed := time.Since(start)
-
- expectedMaximum := time.Second
- if tt.lingerOn {
- expectedMaximum += lingerDuration
- if elapsed < lingerDuration {
- t.Errorf("expected close to take at least %s, but took %s", lingerDuration, elapsed)
- }
- }
- if elapsed >= expectedMaximum {
- t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
- }
-
- 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.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- })
- }
-}
-
-func TestTCPLingerNonEstablished(t *testing.T) {
- dut := testbench.NewDUT(t)
- newFD := dut.Socket(t, unix.AF_INET, unix.SOCK_STREAM, unix.IPPROTO_TCP)
- dut.SetSockLingerOption(t, newFD, lingerDuration, true)
-
- // As the socket is in the initial state, Close() should not linger
- // and return immediately.
- start := time.Now()
- dut.CloseWithErrno(context.Background(), t, newFD)
- elapsed := time.Since(start)
-
- expectedMaximum := time.Second
- if elapsed >= time.Second {
- t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
- }
-}
diff --git a/test/packetimpact/tests/tcp_listen_backlog_test.go b/test/packetimpact/tests/tcp_listen_backlog_test.go
deleted file mode 100644
index e124002f6..000000000
--- a/test/packetimpact/tests/tcp_listen_backlog_test.go
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright 2021 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package tcp_listen_backlog_test
-
-import (
- "bytes"
- "flag"
- "sync"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-// TestTCPListenBacklog tests for a listening endpoint behavior:
-// (1) reply to more SYNs than what is configured as listen backlog
-// (2) ignore ACKs (that complete a handshake) when the accept queue is full
-// (3) ignore incoming SYNs when the accept queue is full
-func TestTCPListenBacklog(t *testing.T) {
- dut := testbench.NewDUT(t)
-
- // This is the number of pending connections before SYN cookies are used.
- const backlog = 10
-
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, backlog)
- defer dut.Close(t, listenFd)
-
- // Fill the SYN queue with connections in SYN-RCVD. We will use these to test
- // that ACKs received while the accept queue is full are ignored.
- var synQueueConns [backlog]testbench.TCPIPv4
- defer func() {
- for i := range synQueueConns {
- synQueueConns[i].Close(t)
- }
- }()
- {
- var wg sync.WaitGroup
- for i := range synQueueConns {
- conn := &synQueueConns[i]
- *conn = dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{})
-
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
- if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
- t.Errorf("%d: expected TCP frame: %s", i, err)
- } else if got, want := *got.Flags, header.TCPFlagSyn|header.TCPFlagAck; got != want {
- t.Errorf("%d: got %s, want %s", i, got, want)
- }
- }(i)
- }
- wg.Wait()
- if t.Failed() {
- t.FailNow()
- }
- }
-
- const payloadLen = 1
- payload := testbench.Payload{Bytes: testbench.GenerateRandomPayload(t, payloadLen)}
-
- // Fill the accept queue with connections established using SYN cookies.
- var synCookieConns [backlog + 1]testbench.TCPIPv4
- defer func() {
- for i := range synCookieConns {
- synCookieConns[i].Close(t)
- }
- }()
- {
- var wg sync.WaitGroup
- for i := range synCookieConns {
- conn := &synCookieConns[i]
- *conn = dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{})
-
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
- if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
- t.Errorf("%d: expected TCP frame: %s", i, err)
- } else if got, want := *got.Flags, header.TCPFlagSyn|header.TCPFlagAck; got != want {
- t.Errorf("%d: got %s, want %s", i, got, want)
- }
- // Send a payload so we can observe the dut ACK.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, &payload)
- if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
- t.Errorf("%d: expected TCP frame: %s", i, err)
- } else if got, want := *got.Flags, header.TCPFlagAck; got != want {
- t.Errorf("%d: got %s, want %s", i, got, want)
- }
- }(i)
- }
- wg.Wait()
- if t.Failed() {
- t.FailNow()
- }
- }
-
- // Send ACKs to complete the handshakes. These are expected to be dropped
- // because the accept queue is full.
- {
- var wg sync.WaitGroup
- for i := range synQueueConns {
- conn := &synQueueConns[i]
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- // Wait for the SYN-ACK to be retransmitted to confirm the ACK was
- // dropped.
- seqNum := uint32(*conn.RemoteSeqNum(t) - 1)
- if got, err := conn.Expect(t, testbench.TCP{SeqNum: &seqNum}, time.Second); err != nil {
- t.Errorf("%d: expected TCP frame: %s", i, err)
- } else if got, want := *got.Flags, header.TCPFlagSyn|header.TCPFlagAck; got != want {
- t.Errorf("%d: got %s, want %s", i, got, want)
- }
- }(i)
- }
-
- wg.Wait()
- if t.Failed() {
- t.FailNow()
- }
- }
-
- // While the accept queue is still full, send an unexpected ACK from a new
- // socket. The listener should reply with an RST.
- func() {
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{})
- defer conn.Close(t)
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
- t.Errorf("expected TCP frame: %s", err)
- } else if got, want := *got.Flags, header.TCPFlagRst; got != want {
- t.Errorf("got %s, want %s", got, want)
- }
- }()
-
- func() {
- // Now initiate a new connection when the accept queue is full.
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{})
- defer conn.Close(t)
- // Expect dut connection to drop the SYN.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
- if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err == nil {
- t.Fatalf("expected no TCP frame, got %s", got)
- }
- }()
-
- // Drain the accept queue.
- {
- var wg sync.WaitGroup
- for i := range synCookieConns {
- conn := &synCookieConns[i]
-
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
-
- fd, _ := dut.Accept(t, listenFd)
- b := dut.Recv(t, fd, payloadLen+1, 0)
- dut.Close(t, fd)
- if !bytes.Equal(b, payload.Bytes) {
- t.Errorf("connection %d: got dut.Recv = %x, want = %x", i, b, payload.Bytes)
- }
-
- if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
- t.Errorf("%d: expected TCP frame: %s", i, err)
- } else if got, want := *got.Flags, header.TCPFlagFin|header.TCPFlagAck; got != want {
- t.Errorf("%d: got %s, want %s", i, got, want)
- }
-
- // Prevent retransmission.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- }(i)
- }
- wg.Wait()
- if t.Failed() {
- t.FailNow()
- }
- }
-
- // Complete the partial connections to move them from the SYN queue to the
- // accept queue. We will use these to test that connections in the accept
- // queue are closed on listener shutdown.
- {
- var wg sync.WaitGroup
- for i := range synQueueConns {
- conn := &synQueueConns[i]
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
-
- tcp := testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}
-
- // Exercise connections with and without pending data.
- if i%2 == 0 {
- // Send ACK with no payload; wait for absence of SYN-ACK retransmit.
- conn.Send(t, tcp)
- if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err == nil {
- t.Errorf("%d: expected no TCP frame, got %s", i, got)
- }
- } else {
- // Send ACK with payload; wait for ACK.
- conn.Send(t, tcp, &payload)
- if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
- t.Errorf("%d: expected TCP frame: %s", i, err)
- } else if got, want := *got.Flags, header.TCPFlagAck; got != want {
- t.Errorf("%d: got %s, want %s", i, got, want)
- }
- }
- }(i)
- }
-
- wg.Wait()
- if t.Failed() {
- t.FailNow()
- }
- }
-
- // The accept queue now has N-1 connections in it. The next incoming SYN will
- // enter the SYN queue, and the one following will use SYN cookies. We test
- // both.
- var connectingConns [2]testbench.TCPIPv4
- defer func() {
- for i := range connectingConns {
- connectingConns[i].Close(t)
- }
- }()
- {
- var wg sync.WaitGroup
- for i := range connectingConns {
- conn := &connectingConns[i]
- *conn = dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{})
-
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
- if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
- t.Errorf("%d: expected TCP frame: %s", i, err)
- } else if got, want := *got.Flags, header.TCPFlagSyn|header.TCPFlagAck; got != want {
- t.Errorf("%d: got %s, want %s", i, got, want)
- }
- }(i)
- }
- wg.Wait()
- if t.Failed() {
- t.FailNow()
- }
- }
-
- dut.Shutdown(t, listenFd, unix.SHUT_RD)
-
- var wg sync.WaitGroup
-
- // Shutdown causes Connections in the accept queue to be closed.
- for i := range synQueueConns {
- conn := &synQueueConns[i]
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
-
- if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
- t.Errorf("%d: expected TCP frame: %s", i, err)
- } else if got, want := *got.Flags, header.TCPFlagRst|header.TCPFlagAck; got != want {
- t.Errorf("%d: got %s, want %s", i, got, want)
- }
- }(i)
- }
-
- for i := range connectingConns {
- conn := &connectingConns[i]
-
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
-
- if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err == nil {
- t.Errorf("%d: expected no TCP frame, got %s", i, got)
- }
- }(i)
- }
-
- wg.Wait()
-}
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 e92e6aa9b..000000000
--- a/test/packetimpact/tests/tcp_network_unreachable_test.go
+++ /dev/null
@@ -1,144 +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"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-// 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)
- clientFD, clientPort := dut.CreateBoundSocket(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
- port := uint16(9001)
- conn := dut.Net.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.
- sa := unix.SockaddrInet4{Port: int(port)}
- copy(sa.Addr[:], dut.Net.LocalIPv4)
- if _, err := dut.ConnectWithErrno(context.Background(), t, clientFD, &sa); err != unix.EINPROGRESS {
- t.Errorf("got connect() = %v, want EINPROGRESS", err)
- }
-
- // Get the SYN.
- tcp, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second)
- if err != nil {
- t.Fatalf("expected SYN: %s", err)
- }
-
- // Send a host unreachable message.
- icmpPayload := testbench.Layers{tcp.Prev(), tcp}
- bytes, err := icmpPayload.ToBytes()
- if err != nil {
- t.Fatalf("got icmpPayload.ToBytes() = (_, %s), want = (_, nil)", err)
- }
-
- layers := conn.CreateFrame(t, nil)
- layers[len(layers)-1] = &testbench.ICMPv4{
- Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable),
- Code: testbench.ICMPv4Code(header.ICMPv4HostUnreachable),
- Payload: bytes,
- }
- conn.SendFrameStateless(t, layers)
-
- if err := getConnectError(t, &dut, clientFD); err != unix.EHOSTUNREACH {
- t.Errorf("got connect() = %v, want EHOSTUNREACH", 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)
- clientFD, clientPort := dut.CreateBoundSocket(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, dut.Net.RemoteIPv6)
- conn := dut.Net.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.
- sa := unix.SockaddrInet6{
- Port: int(conn.SrcPort()),
- ZoneId: dut.Net.RemoteDevID,
- }
- copy(sa.Addr[:], dut.Net.LocalIPv6)
- if _, err := dut.ConnectWithErrno(context.Background(), t, clientFD, &sa); err != unix.EINPROGRESS {
- t.Errorf("got connect() = %v, want EINPROGRESS", err)
- }
-
- // Get the SYN.
- tcp, err := conn.Expect(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second)
- if err != nil {
- t.Fatalf("expected SYN: %s", err)
- }
-
- // Send a host unreachable message.
- icmpPayload := testbench.Layers{tcp.Prev(), tcp}
- bytes, err := icmpPayload.ToBytes()
- if err != nil {
- t.Fatalf("got icmpPayload.ToBytes() = (_, %s), want = (_, nil)", err)
- }
-
- layers := conn.CreateFrame(t, nil)
- layers[len(layers)-1] = &testbench.ICMPv6{
- Type: testbench.ICMPv6Type(header.ICMPv6DstUnreachable),
- Code: testbench.ICMPv6Code(header.ICMPv6NetworkUnreachable),
- Payload: bytes,
- }
- conn.SendFrameStateless(t, layers)
-
- if err := getConnectError(t, &dut, clientFD); err != unix.ENETUNREACH {
- t.Errorf("got connect() = %v, want EHOSTUNREACH", err)
- }
-}
-
-// getConnectError gets the errno generated by the on-going connect attempt on
-// fd. fd must be non-blocking and there must be a connect call to fd which
-// returned EINPROGRESS before. These conditions are guaranteed in this test.
-func getConnectError(t *testing.T, dut *testbench.DUT, fd int32) error {
- t.Helper()
- // We previously got EINPROGRESS form the connect call. We can
- // handle it as explained by connect(2):
- // EINPROGRESS:
- // The socket is nonblocking and the connection cannot be
- // completed immediately. It is possible to select(2) or poll(2)
- // for completion by selecting the socket for writing. After
- // select(2) indicates writability, use getsockopt(2) to read
- // the SO_ERROR option at level SOL_SOCKET to determine
- // whether connect() completed successfully (SO_ERROR is
- // zero) or unsuccessfully (SO_ERROR is one of the usual
- // error codes listed here, explaining the reason for the
- // failure).
- dut.PollOne(t, fd, unix.POLLOUT, 10*time.Second)
- if errno := dut.GetSockOptInt(t, fd, unix.SOL_SOCKET, unix.SO_ERROR); errno != 0 {
- return unix.Errno(errno)
- }
- return nil
-}
diff --git a/test/packetimpact/tests/tcp_noaccept_close_rst_test.go b/test/packetimpact/tests/tcp_noaccept_close_rst_test.go
deleted file mode 100644
index 14eb7d93b..000000000
--- a/test/packetimpact/tests/tcp_noaccept_close_rst_test.go
+++ /dev/null
@@ -1,46 +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.Initialize(flag.CommandLine)
-}
-
-func TestTcpNoAcceptCloseReset(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- conn.Connect(t)
- defer conn.Close(t)
- // We need to wait for POLLIN event on listenFd to know the connection is
- // established. Otherwise there could be a race when we issue the Close
- // command prior to the DUT receiving the last ack of the handshake and
- // it will only respond RST instead of RST+ACK.
- dut.PollOne(t, listenFd, unix.POLLIN, time.Second)
- dut.Close(t, listenFd)
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)}, 1*time.Second); err != nil {
- t.Fatalf("expected a RST-ACK packet but got none: %s", err)
- }
-}
diff --git a/test/packetimpact/tests/tcp_outside_the_window_test.go b/test/packetimpact/tests/tcp_outside_the_window_test.go
deleted file mode 100644
index 0523887d9..000000000
--- a/test/packetimpact/tests/tcp_outside_the_window_test.go
+++ /dev/null
@@ -1,182 +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.Initialize(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 header.TCPFlags
- 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)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
- conn.Connect(t)
- acceptFD, _ := dut.Accept(t, listenFD)
- defer dut.Close(t, acceptFD)
-
- 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.TCPFlags(tt.tcpFlags),
- SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))),
- }, tt.payload...)
- timeout := time.Second
- gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: localSeqNum}, timeout)
- if tt.expectACK && err != nil {
- t.Fatalf("expected an ACK packet within %s but got none: %s", timeout, err)
- }
- // Data packets w/o SYN bits are always acked by Linux. Netstack ACK's data packets
- // always right now. So only send a second segment and test for no ACK for packets
- // with no data.
- if tt.expectACK && tt.payload == nil {
- // Sending another out-of-window segment immediately should not trigger
- // an ACK if less than 500ms(default rate limit for out-of-window ACKs)
- // has passed since the last ACK was sent.
- t.Logf("sending another segment")
- conn.Send(t, testbench.TCP{
- Flags: testbench.TCPFlags(tt.tcpFlags),
- SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))),
- }, tt.payload...)
- timeout := 3 * time.Second
- gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: localSeqNum}, timeout)
- if err == nil {
- t.Fatalf("expected no ACK packet but got one: %s", gotACK)
- }
- }
- if !tt.expectACK && gotACK != nil {
- t.Fatalf("expected no ACK packet within %s but got one: %s", timeout, gotACK)
- }
- })
- }
-}
-
-// TestAckOTWSeqInClosing tests that the DUT should send an ACK with
-// the right ACK number when receiving a packet with OTW Seq number
-// in CLOSING state. https://tools.ietf.org/html/rfc793#page-69
-func TestAckOTWSeqInClosing(t *testing.T) {
- for _, tt := range []struct {
- description string
- flags header.TCPFlags
- payloads testbench.Layers
- 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},
- {"FINACK", header.TCPFlagFin | header.TCPFlagAck, nil, 0, false},
- {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("Sample Data")}}, 0, false},
-
- {"SYN", header.TCPFlagSyn, nil, 1, true},
- {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 1, true},
- {"ACK", header.TCPFlagAck, nil, 1, true},
- {"FINACK", header.TCPFlagFin | header.TCPFlagAck, nil, 1, true},
- {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("Sample Data")}}, 1, true},
-
- {"SYN", header.TCPFlagSyn, nil, 2, true},
- {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 2, true},
- {"ACK", header.TCPFlagAck, nil, 2, true},
- {"FINACK", header.TCPFlagFin | header.TCPFlagAck, nil, 2, true},
- {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("Sample Data")}}, 2, true},
- } {
- t.Run(fmt.Sprintf("%s%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
- conn.Connect(t)
- acceptFD, _ := dut.Accept(t, listenFD)
- defer dut.Close(t, acceptFD)
-
- dut.Shutdown(t, acceptFD, unix.SHUT_WR)
-
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected FINACK from DUT, but got none: %s", err)
- }
-
- // Do not ack the FIN from DUT so that the TCP state on DUT is CLOSING instead of CLOSED.
- seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1)
- conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
-
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected an ACK to our FIN, but got none: %s", err)
- }
-
- windowSize := seqnum.Size(*gotTCP.WindowSize) + tt.seqNumOffset
- conn.SendFrameStateless(t, conn.CreateFrame(t, testbench.Layers{&testbench.TCP{
- SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))),
- AckNum: seqNumForTheirFIN,
- Flags: testbench.TCPFlags(tt.flags),
- }}, tt.payloads...))
-
- gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if tt.expectACK && err != nil {
- t.Errorf("expected an ACK but got none: %s", err)
- }
- if !tt.expectACK && gotACK != nil {
- t.Errorf("expected no ACK but got one: %s", gotACK)
- }
- })
- }
-}
diff --git a/test/packetimpact/tests/tcp_paws_mechanism_test.go b/test/packetimpact/tests/tcp_paws_mechanism_test.go
deleted file mode 100644
index 9054955ea..000000000
--- a/test/packetimpact/tests/tcp_paws_mechanism_test.go
+++ /dev/null
@@ -1,108 +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.Initialize(flag.CommandLine)
-}
-
-func TestPAWSMechanism(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- options := make([]byte, header.TCPOptionTSLength)
- header.EncodeTSOption(currentTS(), 0, options)
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn), Options: options})
- synAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("didn't get synack during handshake: %s", err)
- }
- 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.TCPFlags(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.TCPFlags(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
-
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected an ACK but got none: %s", err)
- }
-
- 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.TCPFlags(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
-
- gotTCP, err = conn.Expect(t, testbench.TCP{AckNum: lastAckNum, Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected segment with AckNum %d but got none: %s", lastAckNum, err)
- }
- 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_send_recv_in_syn_sent_test.go b/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
deleted file mode 100644
index 974c15384..000000000
--- a/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package tcp_queue_send_recv_in_syn_sent_test
-
-import (
- "bytes"
- "context"
- "encoding/hex"
- "errors"
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-// TestQueueSendInSynSentHandshake tests send behavior when the TCP state
-// is SYN-SENT and the connections is finally established.
-func TestQueueSendInSynSentHandshake(t *testing.T) {
- dut := testbench.NewDUT(t)
- socket, remotePort := dut.CreateBoundSocket(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- sampleData := []byte("Sample Data")
-
- dut.SetNonBlocking(t, socket, true)
- if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, unix.EINPROGRESS) {
- t.Fatalf("failed to bring DUT to SYN-SENT, got: %s, want EINPROGRESS", err)
- }
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second); err != nil {
- t.Fatalf("expected a SYN from DUT, but got none: %s", err)
- }
-
- // Test blocking send.
- dut.SetNonBlocking(t, socket, false)
-
- start := make(chan struct{})
- done := make(chan struct{})
- go func() {
- defer close(done)
-
- close(start)
- // Issue SEND call in SYN-SENT, this should be queued for
- // process until the connection is established.
- if _, err := dut.SendWithErrno(context.Background(), t, socket, sampleData, 0); err != unix.Errno(0) {
- t.Errorf("failed to send on DUT: %s", err)
- }
- }()
-
- // Wait for the goroutine to be scheduled and before it
- // blocks on endpoint send/receive.
- <-start
- // The following sleep is used to prevent the connection
- // from being established before we are blocked: there is
- // still a small time window between we sending the RPC
- // request and the system actually being blocked.
- time.Sleep(100 * time.Millisecond)
-
- select {
- case <-done:
- t.Fatal("expected send to be blocked in SYN-SENT")
- default:
- }
-
- // Bring the connection to Established.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)})
-
- <-done
-
- // Expect the data from the DUT's enqueued send request.
- //
- // On Linux, this can be piggybacked with the ACK completing the
- // handshake. On gVisor, getting such a piggyback is a bit more
- // complicated because the actual data enqueuing occurs in the
- // callers of endpoint Write.
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagPsh | header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData}, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagPsh | header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected an ACK from DUT, but got none: %s", err)
- }
-}
-
-// TestQueueRecvInSynSentHandshake tests recv behavior when the TCP state
-// is SYN-SENT and the connections is finally established.
-func TestQueueRecvInSynSentHandshake(t *testing.T) {
- dut := testbench.NewDUT(t)
- socket, remotePort := dut.CreateBoundSocket(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- sampleData := []byte("Sample Data")
-
- dut.SetNonBlocking(t, socket, true)
- if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, unix.EINPROGRESS) {
- t.Fatalf("failed to bring DUT to SYN-SENT, got: %s, want EINPROGRESS", err)
- }
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second); err != nil {
- t.Fatalf("expected a SYN from DUT, but got none: %s", err)
- }
-
- if _, _, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0); err != unix.EWOULDBLOCK {
- t.Fatalf("expected error %s, got %s", unix.EWOULDBLOCK, err)
- }
-
- // Test blocking read.
- dut.SetNonBlocking(t, socket, false)
-
- start := make(chan struct{})
- done := make(chan struct{})
- go func() {
- defer close(done)
-
- close(start)
- // Issue RECEIVE call in SYN-SENT, this should be queued for
- // process until the connection is established.
- n, buff, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0)
- if err != unix.Errno(0) {
- t.Errorf("failed to recv on DUT: %s", err)
- return
- }
- if got := buff[:n]; !bytes.Equal(got, sampleData) {
- t.Errorf("received data doesn't match, got:\n%s, want:\n%s", hex.Dump(got), hex.Dump(sampleData))
- }
- }()
-
- // Wait for the goroutine to be scheduled and before it
- // blocks on endpoint send/receive.
- <-start
-
- // The following sleep is used to prevent the connection
- // from being established before we are blocked: there is
- // still a small time window between we sending the RPC
- // request and the system actually being blocked.
- time.Sleep(100 * time.Millisecond)
-
- // Bring the connection to Established.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected an ACK from DUT, but got none: %s", err)
- }
-
- // Send sample payload so that DUT can recv.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagPsh | header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected an ACK from DUT, but got none: %s", err)
- }
-
- <-done
-}
-
-// TestQueueSendInSynSentRST tests send behavior when the TCP state
-// is SYN-SENT and an RST is sent.
-func TestQueueSendInSynSentRST(t *testing.T) {
- dut := testbench.NewDUT(t)
- socket, remotePort := dut.CreateBoundSocket(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- sampleData := []byte("Sample Data")
-
- dut.SetNonBlocking(t, socket, true)
- if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, unix.EINPROGRESS) {
- t.Fatalf("failed to bring DUT to SYN-SENT, got: %s, want EINPROGRESS", err)
- }
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second); err != nil {
- t.Fatalf("expected a SYN from DUT, but got none: %s", err)
- }
-
- // Test blocking send.
- dut.SetNonBlocking(t, socket, false)
-
- start := make(chan struct{})
- done := make(chan struct{})
- go func() {
- defer close(done)
-
- close(start)
- // Issue SEND call in SYN-SENT, this should be queued for
- // process until the connection is established.
- n, err := dut.SendWithErrno(context.Background(), t, socket, sampleData, 0)
- if err != unix.ECONNREFUSED {
- t.Errorf("expected error %s, got %s", unix.ECONNREFUSED, err)
- }
- if n != -1 {
- t.Errorf("expected return value %d, got %d", -1, n)
- }
- }()
-
- // Wait for the goroutine to be scheduled and before it
- // blocks on endpoint send/receive.
- <-start
-
- // The following sleep is used to prevent the connection
- // from being established before we are blocked: there is
- // still a small time window between we sending the RPC
- // request and the system actually being blocked.
- time.Sleep(100 * time.Millisecond)
-
- select {
- case <-done:
- t.Fatal("expected send to be blocked in SYN-SENT")
- default:
- }
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)})
-
- <-done
-}
-
-// TestQueueRecvInSynSentRST tests recv behavior when the TCP state
-// is SYN-SENT and an RST is sent.
-func TestQueueRecvInSynSentRST(t *testing.T) {
- dut := testbench.NewDUT(t)
- socket, remotePort := dut.CreateBoundSocket(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- sampleData := []byte("Sample Data")
-
- dut.SetNonBlocking(t, socket, true)
- if _, err := dut.ConnectWithErrno(context.Background(), t, socket, conn.LocalAddr(t)); !errors.Is(err, unix.EINPROGRESS) {
- t.Fatalf("failed to bring DUT to SYN-SENT, got: %s, want EINPROGRESS", err)
- }
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second); err != nil {
- t.Fatalf("expected a SYN from DUT, but got none: %s", err)
- }
-
- if _, _, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0); err != unix.EWOULDBLOCK {
- t.Fatalf("expected error %s, got %s", unix.EWOULDBLOCK, err)
- }
-
- // Test blocking read.
- dut.SetNonBlocking(t, socket, false)
-
- start := make(chan struct{})
- done := make(chan struct{})
- go func() {
- defer close(done)
-
- close(start)
- // Issue RECEIVE call in SYN-SENT, this should be queued for
- // process until the connection is established.
- n, _, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0)
- if err != unix.ECONNREFUSED {
- t.Errorf("expected error %s, got %s", unix.ECONNREFUSED, err)
- }
- if n != -1 {
- t.Errorf("expected return value %d, got %d", -1, n)
- }
- }()
-
- // Wait for the goroutine to be scheduled and before it
- // blocks on endpoint send/receive.
- <-start
-
- // The following sleep is used to prevent the connection
- // from being established before we are blocked: there is
- // still a small time window between we sending the RPC
- // request and the system actually being blocked.
- time.Sleep(100 * time.Millisecond)
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)})
- <-done
-}
diff --git a/test/packetimpact/tests/tcp_rack_test.go b/test/packetimpact/tests/tcp_rack_test.go
deleted file mode 100644
index 5a60bf712..000000000
--- a/test/packetimpact/tests/tcp_rack_test.go
+++ /dev/null
@@ -1,404 +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_rack_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/pkg/tcpip/seqnum"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-const (
- // payloadSize is the size used to send packets.
- payloadSize = header.TCPDefaultMSS
-
- // simulatedRTT is the time delay between packets sent and acked to
- // increase the RTT.
- simulatedRTT = 30 * time.Millisecond
-
- // numPktsForRTT is the number of packets sent and acked to establish
- // RTT.
- numPktsForRTT = 10
-)
-
-func createSACKConnection(t *testing.T) (testbench.DUT, testbench.TCPIPv4, int32, int32) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
-
- // Enable SACK.
- opts := make([]byte, 40)
- optsOff := 0
- optsOff += header.EncodeNOP(opts[optsOff:])
- optsOff += header.EncodeNOP(opts[optsOff:])
- optsOff += header.EncodeSACKPermittedOption(opts[optsOff:])
-
- conn.ConnectWithOptions(t, opts[:optsOff])
- acceptFd, _ := dut.Accept(t, listenFd)
- return dut, conn, acceptFd, listenFd
-}
-
-func closeSACKConnection(t *testing.T, dut testbench.DUT, conn testbench.TCPIPv4, acceptFd, listenFd int32) {
- dut.Close(t, acceptFd)
- dut.Close(t, listenFd)
- conn.Close(t)
-}
-
-func getRTTAndRTO(t *testing.T, dut testbench.DUT, acceptFd int32) (rtt, rto time.Duration) {
- info := dut.GetSockOptTCPInfo(t, acceptFd)
- return time.Duration(info.RTT) * time.Microsecond, time.Duration(info.RTO) * time.Microsecond
-}
-
-func sendAndReceive(t *testing.T, dut testbench.DUT, conn testbench.TCPIPv4, numPkts int, acceptFd int32, sendACK bool) time.Time {
- seqNum1 := *conn.RemoteSeqNum(t)
- payload := make([]byte, payloadSize)
- var lastSent time.Time
- for i, sn := 0, seqNum1; i < numPkts; i++ {
- lastSent = time.Now()
- dut.Send(t, acceptFd, payload, 0)
- gotOne, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(sn))}, time.Second)
- if err != nil {
- t.Fatalf("Expect #%d: %s", i+1, err)
- continue
- }
- if gotOne == nil {
- t.Fatalf("#%d: expected a packet within a second but got none", i+1)
- }
- sn.UpdateForward(seqnum.Size(payloadSize))
-
- if sendACK {
- time.Sleep(simulatedRTT)
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(sn))})
- }
- }
- return lastSent
-}
-
-// TestRACKTLPAllPacketsLost tests TLP when an entire flight of data is lost.
-func TestRACKTLPAllPacketsLost(t *testing.T) {
- dut, conn, acceptFd, listenFd := createSACKConnection(t)
- seqNum1 := *conn.RemoteSeqNum(t)
-
- // Send ACK for data packets to establish RTT.
- sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
- seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
-
- // We are not sending ACK for these packets.
- const numPkts = 5
- lastSent := sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
-
- // Probe Timeout (PTO) should be two times RTT. Check that the last
- // packet is retransmitted after probe timeout.
- rtt, _ := getRTTAndRTO(t, dut, acceptFd)
- pto := rtt * 2
- // We expect the 5th packet (the last unacknowledged packet) to be
- // retransmitted.
- tlpProbe := testbench.Uint32(uint32(seqNum1) + uint32((numPkts-1)*payloadSize))
- if _, err := conn.Expect(t, testbench.TCP{SeqNum: tlpProbe}, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s %v %v", err, rtt, pto)
- }
- diff := time.Now().Sub(lastSent)
- if diff < pto {
- t.Fatalf("expected payload was received before the probe timeout, got: %v, want: %v", diff, pto)
- }
- closeSACKConnection(t, dut, conn, acceptFd, listenFd)
-}
-
-// TestRACKTLPLost tests TLP when there are tail losses.
-// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.4
-func TestRACKTLPLost(t *testing.T) {
- dut, conn, acceptFd, listenFd := createSACKConnection(t)
- seqNum1 := *conn.RemoteSeqNum(t)
-
- // Send ACK for data packets to establish RTT.
- sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
- seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
-
- // We are not sending ACK for these packets.
- const numPkts = 10
- lastSent := sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
-
- // Cumulative ACK for #[1-5] packets.
- ackNum := seqNum1.Add(seqnum.Size(6 * payloadSize))
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(ackNum))})
-
- // Probe Timeout (PTO) should be two times RTT. Check that the last
- // packet is retransmitted after probe timeout.
- rtt, _ := getRTTAndRTO(t, dut, acceptFd)
- pto := rtt * 2
- // We expect the 10th packet (the last unacknowledged packet) to be
- // retransmitted.
- tlpProbe := testbench.Uint32(uint32(seqNum1) + uint32((numPkts-1)*payloadSize))
- if _, err := conn.Expect(t, testbench.TCP{SeqNum: tlpProbe}, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- diff := time.Now().Sub(lastSent)
- if diff < pto {
- t.Fatalf("expected payload was received before the probe timeout, got: %v, want: %v", diff, pto)
- }
- closeSACKConnection(t, dut, conn, acceptFd, listenFd)
-}
-
-// TestRACKWithSACK tests that RACK marks the packets as lost after receiving
-// the ACK for retransmitted packets.
-// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-8.1
-func TestRACKWithSACK(t *testing.T) {
- dut, conn, acceptFd, listenFd := createSACKConnection(t)
- seqNum1 := *conn.RemoteSeqNum(t)
-
- // Send ACK for data packets to establish RTT.
- sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
- seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
-
- // We are not sending ACK for these packets.
- const numPkts = 3
- sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
-
- time.Sleep(simulatedRTT)
- // SACK for #2 packet.
- sackBlock := make([]byte, 40)
- start := seqNum1.Add(seqnum.Size(payloadSize))
- end := start.Add(seqnum.Size(payloadSize))
- sbOff := 0
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
- start, end,
- }}, sackBlock[sbOff:])
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
-
- rtt, _ := getRTTAndRTO(t, dut, acceptFd)
- timeout := 2 * rtt
- // RACK marks #1 packet as lost after RTT+reorderWindow(RTT/4) and
- // retransmits it.
- if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, timeout); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-
- time.Sleep(simulatedRTT)
- // ACK for #1 packet.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(end))})
-
- // RACK considers transmission times of the packets to mark them lost.
- // As the 3rd packet was sent before the retransmitted 1st packet, RACK
- // marks it as lost and retransmits it..
- expectedSeqNum := testbench.Uint32(uint32(seqNum1) + uint32((numPkts-1)*payloadSize))
- if _, err := conn.Expect(t, testbench.TCP{SeqNum: expectedSeqNum}, timeout); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- closeSACKConnection(t, dut, conn, acceptFd, listenFd)
-}
-
-// TestRACKWithoutReorder tests that without reordering RACK will retransmit the
-// lost packets after reorder timer expires.
-func TestRACKWithoutReorder(t *testing.T) {
- dut, conn, acceptFd, listenFd := createSACKConnection(t)
- seqNum1 := *conn.RemoteSeqNum(t)
-
- // Send ACK for data packets to establish RTT.
- sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
- seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
-
- // We are not sending ACK for these packets.
- const numPkts = 4
- sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
-
- // SACK for [3,4] packets.
- sackBlock := make([]byte, 40)
- start := seqNum1.Add(seqnum.Size(2 * payloadSize))
- end := start.Add(seqnum.Size(2 * payloadSize))
- sbOff := 0
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
- start, end,
- }}, sackBlock[sbOff:])
- time.Sleep(simulatedRTT)
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
-
- // RACK marks #1 and #2 packets as lost and retransmits both after
- // RTT + reorderWindow. The reorderWindow initially will be a small
- // fraction of RTT.
- rtt, _ := getRTTAndRTO(t, dut, acceptFd)
- timeout := 2 * rtt
- for i, sn := 0, seqNum1; i < 2; i++ {
- if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(sn))}, timeout); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- sn.UpdateForward(seqnum.Size(payloadSize))
- }
- closeSACKConnection(t, dut, conn, acceptFd, listenFd)
-}
-
-// TestRACKWithReorder tests that RACK will retransmit segments when there is
-// reordering in the connection and reorder timer expires.
-func TestRACKWithReorder(t *testing.T) {
- dut, conn, acceptFd, listenFd := createSACKConnection(t)
- seqNum1 := *conn.RemoteSeqNum(t)
-
- // Send ACK for data packets to establish RTT.
- sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
- seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
-
- // We are not sending ACK for these packets.
- const numPkts = 4
- sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
-
- time.Sleep(simulatedRTT)
- // SACK in reverse order for the connection to detect reorder.
- var start seqnum.Value
- var end seqnum.Value
- for i := 0; i < numPkts-1; i++ {
- sackBlock := make([]byte, 40)
- sbOff := 0
- start = seqNum1.Add(seqnum.Size((numPkts - i - 1) * payloadSize))
- end = start.Add(seqnum.Size((i + 1) * payloadSize))
- sackBlock = make([]byte, 40)
- sbOff = 0
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
- start, end,
- }}, sackBlock[sbOff:])
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
- }
-
- // Send a DSACK block indicating both original and retransmitted
- // packets are received, RACK will increase the reordering window on
- // every DSACK.
- dsackBlock := make([]byte, 40)
- dbOff := 0
- start = seqNum1
- end = start.Add(seqnum.Size(2 * payloadSize))
- dbOff += header.EncodeNOP(dsackBlock[dbOff:])
- dbOff += header.EncodeNOP(dsackBlock[dbOff:])
- dbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
- start, end,
- }}, dsackBlock[dbOff:])
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1 + numPkts*payloadSize)), Options: dsackBlock[:dbOff]})
-
- seqNum1.UpdateForward(seqnum.Size(numPkts * payloadSize))
- sendTime := time.Now()
- sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
-
- time.Sleep(simulatedRTT)
- // Send SACK for [2-5] packets.
- sackBlock := make([]byte, 40)
- sbOff := 0
- start = seqNum1.Add(seqnum.Size(payloadSize))
- end = start.Add(seqnum.Size(3 * payloadSize))
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
- start, end,
- }}, sackBlock[sbOff:])
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
-
- // Expect the retransmission of #1 packet after RTT+ReorderWindow.
- if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- rtt, _ := getRTTAndRTO(t, dut, acceptFd)
- diff := time.Now().Sub(sendTime)
- if diff < rtt {
- t.Fatalf("expected payload was received too sonn, within RTT")
- }
-
- closeSACKConnection(t, dut, conn, acceptFd, listenFd)
-}
-
-// TestRACKWithLostRetransmission tests that RACK will not enter RTO when a
-// retransmitted segment is lost and enters fast recovery.
-func TestRACKWithLostRetransmission(t *testing.T) {
- dut, conn, acceptFd, listenFd := createSACKConnection(t)
- seqNum1 := *conn.RemoteSeqNum(t)
-
- // Send ACK for data packets to establish RTT.
- sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
- seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
-
- // We are not sending ACK for these packets.
- const numPkts = 5
- sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
-
- // SACK for [2-5] packets.
- sackBlock := make([]byte, 40)
- start := seqNum1.Add(seqnum.Size(payloadSize))
- end := start.Add(seqnum.Size(4 * payloadSize))
- sbOff := 0
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
- start, end,
- }}, sackBlock[sbOff:])
- time.Sleep(simulatedRTT)
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
-
- // RACK marks #1 packet as lost and retransmits it after
- // RTT + reorderWindow. The reorderWindow is bounded between a small
- // fraction of RTT and 1 RTT.
- rtt, _ := getRTTAndRTO(t, dut, acceptFd)
- timeout := 2 * rtt
- if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, timeout); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-
- // Send #6 packet.
- payload := make([]byte, payloadSize)
- dut.Send(t, acceptFd, payload, 0)
- gotOne, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1 + 5*payloadSize))}, time.Second)
- if err != nil {
- t.Fatalf("Expect #6: %s", err)
- }
- if gotOne == nil {
- t.Fatalf("#6: expected a packet within a second but got none")
- }
-
- // SACK for [2-6] packets.
- sackBlock1 := make([]byte, 40)
- start = seqNum1.Add(seqnum.Size(payloadSize))
- end = start.Add(seqnum.Size(5 * payloadSize))
- sbOff1 := 0
- sbOff1 += header.EncodeNOP(sackBlock1[sbOff1:])
- sbOff1 += header.EncodeNOP(sackBlock1[sbOff1:])
- sbOff1 += header.EncodeSACKBlocks([]header.SACKBlock{{
- start, end,
- }}, sackBlock1[sbOff1:])
- time.Sleep(simulatedRTT)
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock1[:sbOff1]})
-
- // Expect re-retransmission of #1 packet without entering an RTO.
- if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, timeout); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-
- // Check the congestion control state.
- info := dut.GetSockOptTCPInfo(t, acceptFd)
- if info.CaState != linux.TCP_CA_Recovery {
- t.Fatalf("expected connection to be in fast recovery, want: %v got: %v", linux.TCP_CA_Recovery, info.CaState)
- }
-
- closeSACKConnection(t, dut, conn, acceptFd, listenFd)
-}
diff --git a/test/packetimpact/tests/tcp_rcv_buf_space_test.go b/test/packetimpact/tests/tcp_rcv_buf_space_test.go
deleted file mode 100644
index f121d44eb..000000000
--- a/test/packetimpact/tests/tcp_rcv_buf_space_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 tcp_rcv_buf_space_test
-
-import (
- "context"
- "flag"
- "testing"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-// TestReduceRecvBuf tests that a packet within window is still dropped
-// if the available buffer space drops below the size of the incoming
-// segment.
-func TestReduceRecvBuf(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFd, _ := dut.Accept(t, listenFd)
- defer dut.Close(t, acceptFd)
-
- // Set a small receive buffer for the test.
- const rcvBufSz = 4096
- dut.SetSockOptInt(t, acceptFd, unix.SOL_SOCKET, unix.SO_RCVBUF, rcvBufSz)
-
- // Retrieve the actual buffer.
- bufSz := dut.GetSockOptInt(t, acceptFd, unix.SOL_SOCKET, unix.SO_RCVBUF)
-
- // Generate a payload of 1 more than the actual buffer size used by the
- // DUT.
- sampleData := testbench.GenerateRandomPayload(t, int(bufSz)+1)
- // Send and receive sample data to the dut.
- const pktSize = 1400
- for payload := sampleData; len(payload) != 0; {
- payloadBytes := pktSize
- if l := len(payload); l < payloadBytes {
- payloadBytes = l
- }
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, []testbench.Layer{&testbench.Payload{Bytes: payload[:payloadBytes]}}...)
- payload = payload[payloadBytes:]
- }
-
- // First read should read < len(sampleData)
- if ret, _, err := dut.RecvWithErrno(context.Background(), t, acceptFd, int32(len(sampleData)), 0); ret == -1 || int(ret) == len(sampleData) {
- t.Fatalf("dut.RecvWithErrno(ctx, t, %d, %d, 0) = %d,_, %s", acceptFd, int32(len(sampleData)), ret, err)
- }
-
- // Second read should return EAGAIN as the last segment should have been
- // dropped due to it exceeding the receive buffer space available in the
- // socket.
- if ret, got, err := dut.RecvWithErrno(context.Background(), t, acceptFd, int32(len(sampleData)), unix.MSG_DONTWAIT); got != nil || ret != -1 || err != unix.EAGAIN {
- t.Fatalf("expected no packets but got: %s", got)
- }
-}
diff --git a/test/packetimpact/tests/tcp_retransmits_test.go b/test/packetimpact/tests/tcp_retransmits_test.go
deleted file mode 100644
index d3fb789f4..000000000
--- a/test/packetimpact/tests/tcp_retransmits_test.go
+++ /dev/null
@@ -1,94 +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 (
- "bytes"
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-func getRTO(t *testing.T, dut testbench.DUT, acceptFd int32) (rto time.Duration) {
- info := dut.GetSockOptTCPInfo(t, acceptFd)
- return time.Duration(info.RTO) * time.Microsecond
-}
-
-// TestRetransmits tests retransmits occur at exponentially increasing
-// time intervals.
-func TestRetransmits(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFd, _ := dut.Accept(t, listenFd)
- defer dut.Close(t, acceptFd)
-
- dut.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
-
- sampleData := []byte("Sample Data")
- samplePayload := &testbench.Payload{Bytes: sampleData}
-
- // Give a chance for the dut to estimate RTO with RTT from the DATA-ACK.
- // This is to reduce the test run-time from the default initial RTO of 1s.
- // TODO(gvisor.dev/issue/2685) Estimate RTO during handshake, after which
- // we can skip this data send/recv which is solely to estimate RTO.
- dut.Send(t, acceptFd, sampleData, 0)
- if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
- // Wait for the DUT to receive the data, thus ensuring that the stack has
- // estimated RTO before we query RTO via TCP_INFO.
- if got := dut.Recv(t, acceptFd, int32(len(sampleData)), 0); !bytes.Equal(got, sampleData) {
- t.Fatalf("got dut.Recv(t, %d, %d, 0) = %s, want %s", acceptFd, len(sampleData), got, sampleData)
- }
-
- const timeoutCorrection = time.Second
- const diffCorrection = 200 * time.Millisecond
- rto := getRTO(t, dut, acceptFd)
-
- dut.Send(t, acceptFd, sampleData, 0)
- seq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)))
- if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: seq}, samplePayload, rto+timeoutCorrection); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-
- // Expect retransmits of the same segment.
- for i := 0; i < 5; i++ {
- startTime := time.Now()
- rto = getRTO(t, dut, acceptFd)
- if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: seq}, samplePayload, rto+timeoutCorrection); err != nil {
- t.Fatalf("expected payload was not received within %s loop %d err %s", rto+timeoutCorrection, i, err)
- }
- if diff := time.Since(startTime); diff+diffCorrection < rto {
- t.Fatalf("retransmit came sooner got: %s want: >= %s probe %d", diff+diffCorrection, rto, i)
- }
- }
-}
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 64b7288fb..000000000
--- a/test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go
+++ /dev/null
@@ -1,103 +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"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-// 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(tt.description, func(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
-
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort, 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.TCPFlags(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.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh), WindowSize: testbench.Uint16(tt.windowSize)}, &testbench.Payload{Bytes: sampleData})
-
- // Expect the dut to piggyback the ACK for received data along with
- // the segment enqueued for transmit.
- 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_syncookie_test.go b/test/packetimpact/tests/tcp_syncookie_test.go
deleted file mode 100644
index 6be09996b..000000000
--- a/test/packetimpact/tests/tcp_syncookie_test.go
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2021 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package tcp_syncookie_test
-
-import (
- "flag"
- "fmt"
- "math"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-// TestTCPSynCookie tests for ACK handling for connections in SYNRCVD state
-// connections with and without syncookies. It verifies if the passive open
-// connection is indeed using syncookies before proceeding.
-func TestTCPSynCookie(t *testing.T) {
- dut := testbench.NewDUT(t)
- for _, tt := range []struct {
- accept bool
- flags header.TCPFlags
- }{
- {accept: true, flags: header.TCPFlagAck},
- {accept: true, flags: header.TCPFlagAck | header.TCPFlagPsh},
- {accept: false, flags: header.TCPFlagAck | header.TCPFlagSyn},
- {accept: true, flags: header.TCPFlagAck | header.TCPFlagFin},
- {accept: false, flags: header.TCPFlagAck | header.TCPFlagRst},
- {accept: false, flags: header.TCPFlagRst},
- } {
- t.Run(fmt.Sprintf("flags=%s", tt.flags), func(t *testing.T) {
- // Make a copy before parallelizing the test and refer to that
- // within the test. Otherwise, the test reference could be pointing
- // to an incorrect variant based on how it is scheduled.
- test := tt
-
- t.Parallel()
-
- // Listening endpoint accepts one more connection than the listen
- // backlog. Listener starts using syncookies when it sees a new SYN
- // and has backlog size of connections in SYNRCVD state. Keep the
- // listen backlog 1, so that the test can define 2 connections
- // without and with using syncookies.
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
- defer dut.Close(t, listenFD)
-
- var withoutSynCookieConn testbench.TCPIPv4
- var withSynCookieConn testbench.TCPIPv4
-
- for _, conn := range []*testbench.TCPIPv4{&withoutSynCookieConn, &withSynCookieConn} {
- *conn = dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- }
- defer withoutSynCookieConn.Close(t)
- defer withSynCookieConn.Close(t)
-
- // Setup the 2 connections in SYNRCVD state and verify if one of the
- // connection is indeed using syncookies by checking for absence of
- // SYNACK retransmits.
- for _, c := range []struct {
- desc string
- conn *testbench.TCPIPv4
- expectRetransmit bool
- }{
- {desc: "without syncookies", conn: &withoutSynCookieConn, expectRetransmit: true},
- {desc: "with syncookies", conn: &withSynCookieConn, expectRetransmit: false},
- } {
- t.Run(c.desc, func(t *testing.T) {
- // Expect dut connection to have transitioned to SYNRCVD state.
- c.conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
- if _, err := c.conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected SYNACK, but got %s", err)
- }
-
- // If the DUT listener is using syn cookies, it will not retransmit SYNACK.
- got, err := c.conn.ExpectData(t, &testbench.TCP{SeqNum: testbench.Uint32(uint32(*c.conn.RemoteSeqNum(t) - 1)), Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, 2*time.Second)
- if c.expectRetransmit && err != nil {
- t.Fatalf("expected retransmitted SYNACK, but got %s", err)
- }
- if !c.expectRetransmit && err == nil {
- t.Fatalf("expected no retransmitted SYNACK, but got %s", got)
- }
- })
- }
-
- // Check whether ACKs with the given flags completes the handshake.
- for _, c := range []struct {
- desc string
- conn *testbench.TCPIPv4
- }{
- {desc: "with syncookies", conn: &withSynCookieConn},
- {desc: "without syncookies", conn: &withoutSynCookieConn},
- } {
- t.Run(c.desc, func(t *testing.T) {
- pfds := dut.Poll(t, []unix.PollFd{{Fd: listenFD, Events: math.MaxInt16}}, 0 /*timeout*/)
- if got, want := len(pfds), 0; got != want {
- t.Fatalf("dut.Poll(...) = %d, want = %d", got, want)
- }
-
- sampleData := []byte("Sample Data")
- samplePayload := &testbench.Payload{Bytes: sampleData}
-
- c.conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(test.flags)}, samplePayload)
- pfds = dut.Poll(t, []unix.PollFd{{Fd: listenFD, Events: unix.POLLIN}}, time.Second)
- want := 0
- if test.accept {
- want = 1
- }
- if got := len(pfds); got != want {
- t.Fatalf("got dut.Poll(...) = %d, want = %d", got, want)
- }
- // Accept the connection to enable poll on any subsequent connection.
- if test.accept {
- fd, _ := dut.Accept(t, listenFD)
- if test.flags.Contains(header.TCPFlagFin) {
- if dut.Uname.IsLinux() {
- dut.PollOne(t, fd, unix.POLLIN|unix.POLLRDHUP, time.Second)
- } else {
- // TODO(gvisor.dev/issue/6015): Notify POLLIN|POLLRDHUP on incoming FIN.
- dut.PollOne(t, fd, unix.POLLIN, time.Second)
- }
- }
- got := dut.Recv(t, fd, int32(len(sampleData)), 0)
- if diff := cmp.Diff(got, sampleData); diff != "" {
- t.Fatalf("dut.Recv: data mismatch (-want +got):\n%s", diff)
- }
- dut.Close(t, fd)
- }
- })
- }
- })
- }
-}
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 3346d43c4..000000000
--- a/test/packetimpact/tests/tcp_synrcvd_reset_test.go
+++ /dev/null
@@ -1,60 +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.Initialize(flag.CommandLine)
-}
-
-// TestTCPSynRcvdReset tests transition from SYN-RCVD to CLOSED.
-func TestTCPSynRcvdReset(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- // Expect dut connection to have transitioned to SYN-RCVD state.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected SYN-ACK %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)})
-
- // Expect the connection to have transitioned SYN-RCVD to CLOSED.
- //
- // Retransmit the ACK a few times to give time for the DUT to transition to
- // CLOSED. We cannot use TCP_INFO to lookup the state as this is a passive
- // DUT connection.
- for i := 0; i < 5; i++ {
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, nil, time.Second); err != nil {
- t.Logf("retransmit%d ACK as we did not get the expected RST, %s", i, err)
- continue
- }
- return
- }
- t.Fatal("did not receive a TCP RST")
-}
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 fe53e7061..000000000
--- a/test/packetimpact/tests/tcp_synsent_reset_test.go
+++ /dev/null
@@ -1,94 +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"
- "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.Initialize(flag.CommandLine)
-}
-
-// dutSynSentState sets up the dut connection in SYN-SENT state.
-func dutSynSentState(t *testing.T) (*testbench.DUT, *testbench.TCPIPv4, int32, uint16, uint16) {
- t.Helper()
-
- dut := testbench.NewDUT(t)
-
- clientFD, clientPort := dut.CreateBoundSocket(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
- port := uint16(9001)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{SrcPort: &port, DstPort: &clientPort}, testbench.TCP{SrcPort: &clientPort, DstPort: &port})
-
- sa := unix.SockaddrInet4{Port: int(port)}
- copy(sa.Addr[:], dut.Net.LocalIPv4)
- // Bring the dut to SYN-SENT state with a non-blocking connect.
- dut.Connect(t, clientFD, &sa)
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, nil, time.Second); err != nil {
- t.Fatalf("expected SYN\n")
- }
-
- return &dut, &conn, clientFD, port, clientPort
-}
-
-// TestTCPSynSentReset tests RFC793, p67: SYN-SENT to CLOSED transition.
-func TestTCPSynSentReset(t *testing.T) {
- dut, conn, fd, _, _ := dutSynSentState(t)
- defer conn.Close(t)
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)})
- // Expect the connection to have closed.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, nil, time.Second); err != nil {
- t.Fatalf("expected a TCP RST")
- }
- info := dut.GetSockOptTCPInfo(t, fd)
- if got, want := uint32(info.State), linux.TCP_CLOSE; got != want {
- t.Fatalf("got %d want %d", got, want)
- }
-}
-
-// TestTCPSynSentRcvdReset tests RFC793, p70, SYN-SENT to SYN-RCVD to CLOSED
-// transitions.
-func TestTCPSynSentRcvdReset(t *testing.T) {
- dut, c, fd, remotePort, clientPort := dutSynSentState(t)
- defer c.Close(t)
-
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{SrcPort: &remotePort, DstPort: &clientPort}, testbench.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, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected SYN-ACK %s\n", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)})
- // Expect the connection to have transitioned SYN-RCVD to CLOSED.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, nil, time.Second); err != nil {
- t.Fatalf("expected a TCP RST")
- }
- info := dut.GetSockOptTCPInfo(t, fd)
- if got, want := uint32(info.State), linux.TCP_CLOSE; got != want {
- t.Fatalf("got %d want %d", got, want)
- }
-}
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 89037f0a4..000000000
--- a/test/packetimpact/tests/tcp_timewait_reset_test.go
+++ /dev/null
@@ -1,67 +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.Initialize(flag.CommandLine)
-}
-
-// TestTimeWaitReset tests handling of RST when in TIME_WAIT state.
-func TestTimeWaitReset(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFD, _ := dut.Accept(t, listenFD)
-
- // Trigger active close.
- dut.Close(t, acceptFD)
-
- _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected a FIN: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- // Send a FIN, DUT should transition to TIME_WAIT from FIN_WAIT2.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected an ACK for our FIN: %s", err)
- }
-
- // Send a RST, the DUT should transition to CLOSED from TIME_WAIT.
- // This is the default Linux behavior, it can be changed to ignore RSTs via
- // sysctl net.ipv4.tcp_rfc1337.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)})
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- // The DUT should reply with RST to our ACK as the state should have
- // transitioned to CLOSED.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(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 389bfc629..000000000
--- a/test/packetimpact/tests/tcp_unacc_seq_ack_test.go
+++ /dev/null
@@ -1,274 +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"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/pkg/tcpip/seqnum"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-func 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: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, expectAck: true, restoreSeq: true},
- {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, expectAck: true, restoreSeq: true},
- {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, expectAck: true, restoreSeq: true},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, expectAck: true, restoreSeq: false},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, expectAck: false, restoreSeq: true},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, expectAck: false, restoreSeq: true},
- } {
- t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- dut.Accept(t, listenFD)
-
- sampleData := []byte("Sample Data")
- samplePayload := &testbench.Payload{Bytes: sampleData}
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected ack %s", err)
- }
- 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.TCPFlags(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: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, expectAck: false},
- {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, expectAck: true},
- {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, expectAck: true},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, expectAck: false},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, expectAck: true},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, expectAck: true},
- } {
- t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFD, _ := dut.Accept(t, listenFD)
-
- // Send a FIN to DUT to intiate the passive close.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagFin)})
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected an ACK for our fin and DUT should enter CLOSE_WAIT: %s", err)
- }
- 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.TCPFlags(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.TCPFlags(header.TCPFlagAck | header.TCPFlagFin)}, time.Second); err != nil {
- t.Fatalf("expected DUT to send a FIN: %s", err)
- }
- // Ack the FIN from DUT
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- // Send some extra data to DUT
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, samplePayload)
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, time.Second); err != nil {
- t.Fatalf("expected DUT to send an RST: %s", err)
- }
- })
- }
-}
-
-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: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, restoreSeq: true},
- {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, restoreSeq: true},
- {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, restoreSeq: true},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, restoreSeq: false},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, restoreSeq: true},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, restoreSeq: true},
- } {
- t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFD, _ := dut.Accept(t, listenFD)
-
- // Trigger active close.
- dut.Shutdown(t, acceptFD, unix.SHUT_WR)
-
- // Get to FIN_WAIT2
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected a FIN: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(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.TCPFlags(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.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected an ACK for our fin and DUT should enter TIME_WAIT: %s", err)
- }
-
- sendUnaccSeqAck("TIME_WAIT")
- })
- }
-}
-
-func TestSimultaneousCloseUnaccSeqAck(t *testing.T) {
- for _, tt := range []struct {
- description string
- makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
- seqNumOffset seqnum.Size
- expectAck bool
- }{
- {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, expectAck: false},
- {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, expectAck: true},
- {description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, expectAck: true},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, expectAck: false},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, expectAck: true},
- {description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, expectAck: true},
- } {
- t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFD, _ := dut.Accept(t, listenFD)
-
- // Trigger active close.
- dut.Shutdown(t, acceptFD, unix.SHUT_WR)
-
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected a FIN: %s", err)
- }
- // Do not ack the FIN from DUT so that we get to CLOSING.
- seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1)
- conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
-
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Errorf("expected an ACK to our FIN, but got none: %s", err)
- }
-
- sampleData := []byte("Sample Data")
- samplePayload := &testbench.Payload{Bytes: sampleData}
-
- origSeq := uint32(*conn.LocalSeqNum(t))
- // Send a segment with OTW Seq / unacc ACK.
- tcp := tt.makeTestingTCP(t, &conn, tt.seqNumOffset, seqnum.Size(*gotTCP.WindowSize))
- if tt.description == "OTWSeq" {
- // If we generate an OTW Seq segment, make sure we don't acknowledge their FIN so that
- // we stay in CLOSING.
- tcp.AckNum = seqNumForTheirFIN
- }
- conn.Send(t, tcp, samplePayload)
-
- got, err := conn.Expect(t, testbench.TCP{AckNum: testbench.Uint32(origSeq), Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if tt.expectAck && err != nil {
- t.Errorf("expected an ack in CLOSING state, but got none: %s", err)
- }
- if !tt.expectAck && got != nil {
- t.Errorf("expected no ack in CLOSING state, but got one: %s", got)
- }
- })
- }
-}
diff --git a/test/packetimpact/tests/tcp_user_timeout_test.go b/test/packetimpact/tests/tcp_user_timeout_test.go
deleted file mode 100644
index ef38bd738..000000000
--- a/test/packetimpact/tests/tcp_user_timeout_test.go
+++ /dev/null
@@ -1,99 +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.Initialize(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.TCPFlags(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)
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFD)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
- conn.Connect(t)
- acceptFD, _ := dut.Accept(t, listenFD)
-
- 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.TCPFlags(header.TCPFlagAck)})
-
- // If TCP_USER_TIMEOUT was set and the above delay was longer than the
- // TCP_USER_TIMEOUT then the DUT should send a RST in response to the
- // testbench's packet.
- expectRST := tt.userTimeout != 0 && tt.sendDelay > tt.userTimeout
- expectTimeout := 5 * time.Second
- got, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(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 0d65a2ea2..000000000
--- a/test/packetimpact/tests/tcp_window_shrink_test.go
+++ /dev/null
@@ -1,72 +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.Initialize(flag.CommandLine)
-}
-
-func TestWindowShrink(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFd, _ := dut.Accept(t, listenFd)
- defer dut.Close(t, acceptFd)
-
- dut.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.TCPFlags(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.TCPFlags(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
-
- dut.Send(t, acceptFd, []byte("Sample Data"), 0)
- // Note: There is another kind of zero-window probing which Windows uses (by sending one
- // 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_receive_window_test.go b/test/packetimpact/tests/tcp_zero_receive_window_test.go
deleted file mode 100644
index bd33a2a03..000000000
--- a/test/packetimpact/tests/tcp_zero_receive_window_test.go
+++ /dev/null
@@ -1,202 +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_receive_window_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.Initialize(flag.CommandLine)
-}
-
-// TestZeroReceiveWindow tests if the DUT sends a zero receive window eventually.
-func TestZeroReceiveWindow(t *testing.T) {
- for _, payloadLen := range []int{64, 512, 1024} {
- t.Run(fmt.Sprintf("TestZeroReceiveWindow_with_%dbytes_payload", payloadLen), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFd, _ := dut.Accept(t, listenFd)
- defer dut.Close(t, acceptFd)
-
- dut.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
-
- fillRecvBuffer(t, &conn, &dut, acceptFd, payloadLen)
- })
- }
-}
-
-func fillRecvBuffer(t *testing.T, conn *testbench.TCPIPv4, dut *testbench.DUT, acceptFd int32, payloadLen int) {
- // Expect the DUT to eventually advertise zero receive window.
- // The test would timeout otherwise.
- for readOnce := false; ; {
- samplePayload := &testbench.Payload{Bytes: testbench.GenerateRandomPayload(t, payloadLen)}
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
- // Read once to trigger the subsequent window update from the
- // DUT to grow the right edge of the receive window from what
- // was advertised in the SYN-ACK. This ensures that we test
- // for the full default buffer size (1MB on gVisor at the time
- // of writing this comment), thus testing for cases when the
- // scaled receive window size ends up > 65535 (0xffff).
- if !readOnce {
- if got := dut.Recv(t, acceptFd, int32(payloadLen), 0); len(got) != payloadLen {
- t.Fatalf("got dut.Recv(t, %d, %d, 0) = %d, want %d", acceptFd, payloadLen, len(got), payloadLen)
- }
- readOnce = true
- }
- windowSize := *gotTCP.WindowSize
- t.Logf("got window size = %d", windowSize)
- if windowSize == 0 {
- break
- }
- if payloadLen > int(windowSize) {
- payloadLen = int(windowSize)
- }
- }
-}
-
-func TestZeroToNonZeroWindowUpdate(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
- synAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("didn't get synack during handshake: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
-
- acceptFd, _ := dut.Accept(t, listenFd)
- defer dut.Close(t, acceptFd)
-
- dut.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
-
- mss := header.ParseSynOptions(synAck.Options, true).MSS
- fillRecvBuffer(t, &conn, &dut, acceptFd, int(mss))
-
- // Read < mss worth of data from the receive buffer and expect the DUT to
- // not send a non-zero window update.
- payloadLen := mss - 1
- if got := dut.Recv(t, acceptFd, int32(payloadLen), 0); len(got) != int(payloadLen) {
- t.Fatalf("got dut.Recv(t, %d, %d, 0) = %d, want %d", acceptFd, payloadLen, len(got), payloadLen)
- }
- // Send a zero-window-probe to force an ACK from the receiver with any
- // window updates.
- conn.Send(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(*conn.LocalSeqNum(t) - 1)), Flags: testbench.TCPFlags(header.TCPFlagAck)})
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
- if windowSize := *gotTCP.WindowSize; windowSize != 0 {
- t.Fatalf("got non zero window = %d", windowSize)
- }
-
- // Now, ensure that the DUT eventually sends non-zero window update.
- seqNum := testbench.Uint32(uint32(*conn.LocalSeqNum(t) - 1))
- ackNum := testbench.Uint32(uint32(*conn.LocalSeqNum(t)))
- recvCheckWindowUpdate := func(readLen int) uint16 {
- if got := dut.Recv(t, acceptFd, int32(readLen), 0); len(got) != readLen {
- t.Fatalf("got dut.Recv(t, %d, %d, 0) = %d, want %d", acceptFd, readLen, len(got), readLen)
- }
- conn.Send(t, testbench.TCP{SeqNum: seqNum, Flags: testbench.TCPFlags(header.TCPFlagPsh | header.TCPFlagAck)}, &testbench.Payload{Bytes: make([]byte, 1)})
- gotTCP, err := conn.Expect(t, testbench.TCP{AckNum: ackNum, Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
- return *gotTCP.WindowSize
- }
-
- if !dut.Uname.IsLinux() {
- if win := recvCheckWindowUpdate(1); win == 0 {
- t.Fatal("expected non-zero window update")
- }
- } else {
- // Linux stack takes additional socket reads to send out window update,
- // its a function of sysctl_tcp_rmem among other things.
- // https://github.com/torvalds/linux/blob/7acac4b3196/net/ipv4/tcp_input.c#L687
- for {
- if win := recvCheckWindowUpdate(int(payloadLen)); win != 0 {
- break
- }
- }
- }
-}
-
-// TestNonZeroReceiveWindow tests for the DUT to never send a zero receive
-// window when the data is being read from the socket buffer.
-func TestNonZeroReceiveWindow(t *testing.T) {
- for _, payloadLen := range []int{64, 512, 1024} {
- t.Run(fmt.Sprintf("TestZeroReceiveWindow_with_%dbytes_payload", payloadLen), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFd, _ := dut.Accept(t, listenFd)
- defer dut.Close(t, acceptFd)
-
- dut.SetSockOptInt(t, acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
-
- samplePayload := &testbench.Payload{Bytes: testbench.GenerateRandomPayload(t, payloadLen)}
- var rcvWindow uint16
- initRcv := false
- // This loop keeps a running rcvWindow value from the initial ACK for the data
- // we sent. Once we have received ACKs with non-zero receive windows, we break
- // the loop.
- for {
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
- if got := dut.Recv(t, acceptFd, int32(payloadLen), 0); len(got) != payloadLen {
- t.Fatalf("got dut.Recv(t, %d, %d, 0) = %d, want %d", acceptFd, payloadLen, len(got), payloadLen)
- }
- if *gotTCP.WindowSize == 0 {
- t.Fatalf("expected non-zero receive window.")
- }
- if !initRcv {
- rcvWindow = uint16(*gotTCP.WindowSize)
- initRcv = true
- }
- if rcvWindow <= uint16(payloadLen) {
- break
- }
- rcvWindow -= uint16(payloadLen)
- }
- })
- }
-}
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 22b17a39e..000000000
--- a/test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go
+++ /dev/null
@@ -1,115 +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 (
- "bytes"
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-// TestZeroWindowProbeRetransmit tests retransmits of zero window probes
-// to be sent at exponentially inreasing time intervals.
-func TestZeroWindowProbeRetransmit(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFd, _ := dut.Accept(t, listenFd)
- defer dut.Close(t, acceptFd)
-
- dut.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)
- }
-
- // 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 along with a payload.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh), WindowSize: testbench.Uint16(0)}, samplePayload)
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
- // Wait for the payload to be received by the DUT, which is also an
- // indication of receive of the peer window advertisement.
- if got := dut.Recv(t, acceptFd, int32(len(sampleData)), 0); !bytes.Equal(got, sampleData) {
- t.Fatalf("got dut.Recv(t, %d, %d, 0) = %s, want %s", acceptFd, len(sampleData), got, sampleData)
- }
-
- probeSeq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t) - 1))
- ackProbe := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)))
-
- // Ask the dut to send out data.
- dut.Send(t, acceptFd, sampleData, 0)
-
- var prev time.Duration
- // Expect the dut to keep the connection alive as long as the remote is
- // acknowledging the zero-window probes.
- for i := 1; 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, time.Duration(i)*time.Second); err != nil {
- t.Fatalf("%d: expected a probe with sequence number %d: %s", i, probeSeq, err)
- }
- if i == 1 {
- // Skip the first probe as computing transmit time for that is
- // non-deterministic because of the arbitrary time taken for
- // the dut to receive a send command and issue a send.
- continue
- }
-
- // Check if the time taken to receive the probe from the dut is
- // increasing exponentially. To avoid flakes, use a correction
- // factor for the expected duration which accounts for any
- // scheduling non-determinism.
- const timeCorrection = 200 * time.Millisecond
- got := time.Since(start)
- if want := (2 * prev) - timeCorrection; prev != 0 && got < want {
- t.Errorf("got zero probe %d after %s, want >= %s", i, got, want)
- }
- prev = got
- // Acknowledge the zero-window probes from the dut.
- conn.Send(t, testbench.TCP{AckNum: ackProbe, Flags: testbench.TCPFlags(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
- }
- // Advertize non-zero window.
- conn.Send(t, testbench.TCP{AckNum: ackProbe, Flags: testbench.TCPFlags(header.TCPFlagAck)})
- // Expect the dut to recover and transmit data.
- if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: ackProbe}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-}
diff --git a/test/packetimpact/tests/tcp_zero_window_probe_test.go b/test/packetimpact/tests/tcp_zero_window_probe_test.go
deleted file mode 100644
index 8b90fcbe9..000000000
--- a/test/packetimpact/tests/tcp_zero_window_probe_test.go
+++ /dev/null
@@ -1,111 +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.Initialize(flag.CommandLine)
-}
-
-// TestZeroWindowProbe tests few cases of zero window probing over the
-// same connection.
-func TestZeroWindowProbe(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFd, _ := dut.Accept(t, listenFd)
- defer dut.Close(t, acceptFd)
-
- dut.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.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
-
- // 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.TCPFlags(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.TCPFlags(header.TCPFlagAck)})
- // Expect the dut to recover and transmit data.
- if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: ackProbe}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-
- // 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.TCPFlags(header.TCPFlagAck), SeqNum: testbench.Uint32(uint32(*conn.LocalSeqNum(t) - 1))})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: p}, nil, time.Second); err != nil {
- t.Fatalf("expected a packet with ack number: %d: %s", p, err)
- }
-}
diff --git a/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go b/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go
deleted file mode 100644
index 1ce4d22b7..000000000
--- a/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go
+++ /dev/null
@@ -1,97 +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.Initialize(flag.CommandLine)
-}
-
-// TestZeroWindowProbeUserTimeout sanity tests user timeout when we are
-// retransmitting zero window probes.
-func TestZeroWindowProbeUserTimeout(t *testing.T) {
- dut := testbench.NewDUT(t)
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
- conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFd, _ := dut.Accept(t, listenFd)
- defer dut.Close(t, acceptFd)
-
- dut.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.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
-
- // 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.TCPFlags(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.TCPFlags(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
- // Ask the dut to send out data that would trigger zero window probe retransmissions.
- dut.Send(t, acceptFd, sampleData, 0)
-
- // 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.TCPFlags(header.TCPFlagAck)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, nil, time.Second); err != nil {
- t.Fatalf("expected a TCP RST")
- }
-}
diff --git a/test/packetimpact/tests/udp_any_addr_recv_unicast_test.go b/test/packetimpact/tests/udp_any_addr_recv_unicast_test.go
deleted file mode 100644
index f4ae00a81..000000000
--- a/test/packetimpact/tests/udp_any_addr_recv_unicast_test.go
+++ /dev/null
@@ -1,50 +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.Initialize(flag.CommandLine)
-}
-
-func TestAnyRecvUnicastUDP(t *testing.T) {
- dut := testbench.NewDUT(t)
- boundFD, remotePort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.IPv4zero)
- defer dut.Close(t, boundFD)
- conn := dut.Net.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(dut.Net.RemoteIPv4))},
- 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 f63cfcc9a..000000000
--- a/test/packetimpact/tests/udp_discard_mcast_source_addr_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 udp_discard_mcast_source_addr_test
-
-import (
- "context"
- "flag"
- "fmt"
- "net"
- "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.Initialize(flag.CommandLine)
-}
-
-func TestDiscardsUDPPacketsWithMcastSourceAddressV4(t *testing.T) {
- dut := testbench.NewDUT(t)
- remoteFD, remotePort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, dut.Net.RemoteIPv4)
- defer dut.Close(t, remoteFD)
- dut.SetSockOptTimeval(t, remoteFD, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &oneSecond)
- conn := dut.Net.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 != unix.EAGAIN || errno != unix.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)
- remoteFD, remotePort := dut.CreateBoundSocket(t, unix.SOCK_DGRAM, unix.IPPROTO_UDP, dut.Net.RemoteIPv6)
- defer dut.Close(t, remoteFD)
- dut.SetSockOptTimeval(t, remoteFD, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &oneSecond)
- conn := dut.Net.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 != unix.EAGAIN || errno != unix.EWOULDBLOCK {
- t.Errorf("Recv got unexpected result, ret=%d, payload=%q, errno=%s", ret, payload, errno)
- }
- })
- }
-}
diff --git a/test/packetimpact/tests/udp_icmp_error_propagation_test.go b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
deleted file mode 100644
index 556cee1d9..000000000
--- a/test/packetimpact/tests/udp_icmp_error_propagation_test.go
+++ /dev/null
@@ -1,352 +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"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.Initialize(flag.CommandLine)
-}
-
-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(payload []byte) *testbench.ICMPv4 {
- switch e {
- case portUnreachable:
- return &testbench.ICMPv4{
- Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable),
- Code: testbench.ICMPv4Code(header.ICMPv4PortUnreachable),
- Payload: payload,
- }
- case timeToLiveExceeded:
- return &testbench.ICMPv4{
- Type: testbench.ICMPv4Type(header.ICMPv4TimeExceeded),
- Code: testbench.ICMPv4Code(header.ICMPv4TTLExceeded),
- Payload: payload,
- }
- }
- 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 unix.Errno
-}
-
-// wantErrno computes the errno to expect given the connection mode of a UDP
-// socket and the ICMP error it will receive.
-func wantErrno(c connectionMode, icmpErr icmpError) unix.Errno {
- if c && icmpErr == portUnreachable {
- return unix.ECONNREFUSED
- }
- return unix.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()
-
- 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
- }
-
- icmpPayload := testbench.Layers{ip, udp}
- bytes, err := icmpPayload.ToBytes()
- if err != nil {
- t.Fatalf("got icmpPayload.ToBytes() = (_, %s), want = (_, nil)", err)
- }
-
- layers := conn.CreateFrame(t, nil)
- layers[len(layers)-1] = icmpErr.ToICMPv4(bytes)
- conn.SendFrameStateless(t, layers)
-}
-
-// testRecv tests observing the ICMP error through the recv unix. A packet
-// is sent to the DUT, and if wantErrno is non-zero, then the first recv should
-// fail and the second should succeed. Otherwise if wantErrno is zero then the
-// first recv should succeed immediately.
-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 != unix.Errno(0) {
- 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 succeed; 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 != unix.Errno(0) {
- 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 := unix.Errno(d.dut.GetSockOptInt(t, d.cleanFD, unix.SOL_SOCKET, unix.SO_ERROR)); errno != unix.Errno(0) {
- t.Fatalf("unexpected error (%[1]d) %[1]v on clean socket", errno)
- }
-
- if errno := unix.Errno(d.dut.GetSockOptInt(t, d.remoteFD, unix.SOL_SOCKET, unix.SO_ERROR)); errno != d.wantErrno {
- t.Fatalf("SO_ERROR sockopt after ICMP error is (%[1]d) %[1]v, expected (%[2]d) %[2]v", errno, d.wantErrno)
- }
-
- // 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{
- {"SendTo", false, testSendTo},
- // Send to an address that's different from the one that caused an ICMP
- // error to be returned.
- {"SendToValid", true, testSendTo},
- {"Recv", false, testRecv},
- {"SockOpt", false, testSockOpt},
- } {
- t.Run(fmt.Sprintf("%s/%s/%s", connect, icmpErr, errDetect.name), func(t *testing.T) {
- dut := testbench.NewDUT(t)
-
- 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 := dut.Net.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 := dut.Net.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)
-
- 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 := dut.Net.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 != unix.Errno(0) {
- ret, _, err := dut.RecvWithErrno(context.Background(), 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
- }
- }
-
- if ret, _, err := dut.RecvWithErrno(context.Background(), t, remoteFD, 100, 0); ret == -1 {
- t.Errorf("recv after ICMP error failed with (%[1]d) %[1]", err)
- }
- }()
-
- go func() {
- defer wg.Done()
-
- if ret, _, err := dut.RecvWithErrno(context.Background(), 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()
- })
- }
- }
-}