diff options
Diffstat (limited to 'test/packetimpact/tests')
-rw-r--r-- | test/packetimpact/tests/BUILD | 20 | ||||
-rw-r--r-- | test/packetimpact/tests/ipv4_fragment_reassembly_test.go | 65 | ||||
-rw-r--r-- | test/packetimpact/tests/ipv6_fragment_icmp_error_test.go | 3 | ||||
-rw-r--r-- | test/packetimpact/tests/ipv6_fragment_reassembly_test.go | 52 | ||||
-rw-r--r-- | test/packetimpact/tests/tcp_zero_receive_window_test.go | 125 | ||||
-rw-r--r-- | test/packetimpact/tests/udp_recv_mcast_bcast_test.go | 2 |
6 files changed, 248 insertions, 19 deletions
diff --git a/test/packetimpact/tests/BUILD b/test/packetimpact/tests/BUILD index 33bd070c1..b1b3c578b 100644 --- a/test/packetimpact/tests/BUILD +++ b/test/packetimpact/tests/BUILD @@ -366,9 +366,29 @@ packetimpact_testbench( ], ) +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", + ], +) + validate_all_tests() [packetimpact_go_test( name = t.name, 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 = [ + "manual", + "packetimpact", + ], + tests = existing_rules(), +) diff --git a/test/packetimpact/tests/ipv4_fragment_reassembly_test.go b/test/packetimpact/tests/ipv4_fragment_reassembly_test.go index e00a7aba2..d2203082d 100644 --- a/test/packetimpact/tests/ipv4_fragment_reassembly_test.go +++ b/test/packetimpact/tests/ipv4_fragment_reassembly_test.go @@ -34,10 +34,10 @@ type fragmentInfo struct { offset uint16 size uint16 more uint8 + id uint16 } func TestIPv4FragmentReassembly(t *testing.T) { - const fragmentID = 42 icmpv4ProtoNum := uint8(header.ICMPv4ProtocolNumber) tests := []struct { @@ -45,28 +45,75 @@ func TestIPv4FragmentReassembly(t *testing.T) { ipPayloadLen int fragments []fragmentInfo expectReply bool + skip bool + skipReason string }{ { description: "basic reassembly", - ipPayloadLen: 2000, + ipPayloadLen: 3000, fragments: []fragmentInfo{ - {offset: 0, size: 1000, more: header.IPv4FlagMoreFragments}, - {offset: 1000, size: 1000, more: 0}, + {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: 2000, + ipPayloadLen: 3000, fragments: []fragmentInfo{ - {offset: 1000, size: 1000, more: 0}, - {offset: 0, size: 1000, more: header.IPv4FlagMoreFragments}, + {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, + skip: true, + skipReason: "gvisor.dev/issues/4971", + }, + { + 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, + skip: true, + skipReason: "gvisor.dev/issues/4971", + }, + { + 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, + skip: true, + skipReason: "gvisor.dev/issues/4971", + }, } for _, test := range tests { + if test.skip { + t.Skip("%s test skipped: %s", test.description, test.skipReason) + continue + } t.Run(test.description, func(t *testing.T) { dut := testbench.NewDUT(t) conn := dut.Net.NewIPv4Conn(t, testbench.IPv4{}, testbench.IPv4{}) @@ -95,7 +142,7 @@ func TestIPv4FragmentReassembly(t *testing.T) { Protocol: &icmpv4ProtoNum, FragmentOffset: testbench.Uint16(fragment.offset), Flags: testbench.Uint8(fragment.more), - ID: testbench.Uint16(fragmentID), + ID: testbench.Uint16(fragment.id), }, &testbench.Payload{ Bytes: data[fragment.offset:][:fragment.size], @@ -114,7 +161,7 @@ func TestIPv4FragmentReassembly(t *testing.T) { }, time.Second) if err != nil { // Either an unexpected frame was received, or none at all. - if bytesReceived < test.ipPayloadLen { + 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 diff --git a/test/packetimpact/tests/ipv6_fragment_icmp_error_test.go b/test/packetimpact/tests/ipv6_fragment_icmp_error_test.go index 0ddc1526f..a37867e85 100644 --- a/test/packetimpact/tests/ipv6_fragment_icmp_error_test.go +++ b/test/packetimpact/tests/ipv6_fragment_icmp_error_test.go @@ -119,6 +119,7 @@ func TestIPv6ICMPEchoRequestFragmentReassembly(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + t.Parallel() dut := testbench.NewDUT(t) ipv6Conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{}) conn := (*testbench.Connection)(&ipv6Conn) @@ -220,6 +221,7 @@ func TestIPv6FragmentReassemblyTimeout(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + t.Parallel() dut := testbench.NewDUT(t) ipv6Conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{}) conn := (*testbench.Connection)(&ipv6Conn) @@ -315,6 +317,7 @@ func TestIPv6FragmentParamProblem(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + t.Parallel() dut := testbench.NewDUT(t) ipv6Conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{}) conn := (*testbench.Connection)(&ipv6Conn) diff --git a/test/packetimpact/tests/ipv6_fragment_reassembly_test.go b/test/packetimpact/tests/ipv6_fragment_reassembly_test.go index 65f742f55..dd98ee7a1 100644 --- a/test/packetimpact/tests/ipv6_fragment_reassembly_test.go +++ b/test/packetimpact/tests/ipv6_fragment_reassembly_test.go @@ -35,10 +35,10 @@ type fragmentInfo struct { offset uint16 size uint16 more bool + id uint32 } func TestIPv6FragmentReassembly(t *testing.T) { - const fragmentID = 42 icmpv6ProtoNum := header.IPv6ExtensionHeaderIdentifier(header.ICMPv6ProtocolNumber) tests := []struct { @@ -49,10 +49,11 @@ func TestIPv6FragmentReassembly(t *testing.T) { }{ { description: "basic reassembly", - ipPayloadLen: 1500, + ipPayloadLen: 3000, fragments: []fragmentInfo{ - {offset: 0, size: 760, more: true}, - {offset: 760, size: 740, more: false}, + {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, }, @@ -60,12 +61,45 @@ func TestIPv6FragmentReassembly(t *testing.T) { description: "out of order fragments", ipPayloadLen: 3000, fragments: []fragmentInfo{ - {offset: 0, size: 1024, more: true}, - {offset: 2048, size: 952, more: false}, - {offset: 1024, size: 1024, more: true}, + {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 { @@ -101,7 +135,7 @@ func TestIPv6FragmentReassembly(t *testing.T) { NextHeader: &icmpv6ProtoNum, FragmentOffset: testbench.Uint16(fragment.offset / header.IPv6FragmentExtHdrFragmentOffsetBytesPerUnit), MoreFragments: testbench.Bool(fragment.more), - Identification: testbench.Uint32(fragmentID), + Identification: testbench.Uint32(fragment.id), }, &testbench.Payload{ Bytes: data[fragment.offset:][:fragment.size], @@ -118,7 +152,7 @@ func TestIPv6FragmentReassembly(t *testing.T) { }, time.Second) if err != nil { // Either an unexpected frame was received, or none at all. - if bytesReceived < test.ipPayloadLen { + 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 diff --git a/test/packetimpact/tests/tcp_zero_receive_window_test.go b/test/packetimpact/tests/tcp_zero_receive_window_test.go new file mode 100644 index 000000000..d06690705 --- /dev/null +++ b/test/packetimpact/tests/tcp_zero_receive_window_test.go @@ -0,0 +1,125 @@ +// 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) + + samplePayload := &testbench.Payload{Bytes: testbench.GenerateRandomPayload(t, payloadLen)} + // Expect the DUT to eventually advertise zero receive window. + // The test would timeout otherwise. + for readOnce := false; ; { + conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload) + gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second) + 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 + } + } + }) + } +} + +// 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.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload) + gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second) + if err != nil { + t.Fatalf("expected 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/udp_recv_mcast_bcast_test.go b/test/packetimpact/tests/udp_recv_mcast_bcast_test.go index 71cde6cde..b29c07825 100644 --- a/test/packetimpact/tests/udp_recv_mcast_bcast_test.go +++ b/test/packetimpact/tests/udp_recv_mcast_bcast_test.go @@ -44,7 +44,7 @@ func TestUDPRecvMcastBcast(t *testing.T) { {bound: subnetBcastAddr, to: subnetBcastAddr}, - // FIXME(gvisor.dev/issues/4896): Previously by the time subnetBcastAddr is + // FIXME(gvisor.dev/issue/4896): Previously by the time subnetBcastAddr is // created, IPv4PrefixLength is still 0 because genPseudoFlags is not called // yet, it was only called in NewDUT, so the test didn't do what the author // original intended to and becomes failing because we process all flags at |