Add test for reordering.

Tests the effect of reordering on retransmission and window size.

Test covers the expected behavior of both Linux and netstack, however, netstack
does not behave as expected. Further, the current expected behavior of netstack
is not ideal and should be adjusted in the future.

PiperOrigin-RevId: 316015184
This commit is contained in:
Ian Gudger
2020-06-11 18:04:43 -07:00
committed by gVisor bot
parent fbe41987c9
commit dc4e0157ef
5 changed files with 192 additions and 1 deletions
+1 -1
View File
@@ -150,7 +150,7 @@ message SendRequest {
message SendResponse {
int32 ret = 1;
int32 errno_ = 2;
int32 errno_ = 2; // "errno" may fail to compile in c++.
}
message SendToRequest {
@@ -268,6 +268,7 @@ func TestOne(t *testing.T) {
"--remote_ipv6", remoteIPv6.String(),
"--remote_mac", remoteMAC.String(),
"--device", testNetDev,
"--dut_type", *dutPlatform,
)
_, err = testbench.Exec(dockerutil.RunOpts{}, testArgs...)
if !*expectFailure && err != nil {
+3
View File
@@ -25,6 +25,8 @@ import (
)
var (
// DUTType is the type of device under test.
DUTType = ""
// Device is the local device on the test network.
Device = ""
// LocalIPv4 is the local IPv4 address on the test network.
@@ -63,6 +65,7 @@ func RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(&RemoteIPv6, "remote_ipv6", RemoteIPv6, "remote IPv6 address for test packets")
fs.StringVar(&RemoteMAC, "remote_mac", RemoteMAC, "remote mac address for test packets")
fs.StringVar(&Device, "device", Device, "local device for test packets")
fs.StringVar(&DUTType, "dut_type", DUTType, "type of device under test")
}
// genPseudoFlags populates flag-like global config based on real flags.
+13
View File
@@ -52,6 +52,19 @@ packetimpact_go_test(
],
)
packetimpact_go_test(
name = "tcp_reordering",
srcs = ["tcp_reordering_test.go"],
# TODO(b/139368047): Fix netstack then remove the line below.
expect_netstack_failure = True,
deps = [
"//pkg/tcpip/header",
"//pkg/tcpip/seqnum",
"//test/packetimpact/testbench",
"@org_golang_x_sys//unix:go_default_library",
],
)
packetimpact_go_test(
name = "tcp_window_shrink",
srcs = ["tcp_window_shrink_test.go"],
@@ -0,0 +1,174 @@
// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package reordering_test
import (
"flag"
"testing"
"time"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
)
func init() {
tb.RegisterFlags(flag.CommandLine)
}
func TestReorderingWindow(t *testing.T) {
dut := tb.NewDUT(t)
defer dut.TearDown()
listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
defer dut.Close(listenFd)
conn := tb.NewTCPIPv4(t, tb.TCP{DstPort: &remotePort}, tb.TCP{SrcPort: &remotePort})
defer conn.Close()
// Enable SACK.
opts := make([]byte, 40)
optsOff := 0
optsOff += header.EncodeNOP(opts[optsOff:])
optsOff += header.EncodeNOP(opts[optsOff:])
optsOff += header.EncodeSACKPermittedOption(opts[optsOff:])
// Ethernet guarantees that the MTU is at least 1500 bytes.
const minMTU = 1500
const mss = minMTU - header.IPv4MinimumSize - header.TCPMinimumSize
optsOff += header.EncodeMSSOption(mss, opts[optsOff:])
conn.ConnectWithOptions(opts[:optsOff])
acceptFd, _ := dut.Accept(listenFd)
defer dut.Close(acceptFd)
if tb.DUTType == "linux" {
// Linux has changed its handling of reordering, force the old behavior.
dut.SetSockOpt(acceptFd, unix.IPPROTO_TCP, unix.TCP_CONGESTION, []byte("reno"))
}
pls := dut.GetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_MAXSEG)
if tb.DUTType == "netstack" {
// netstack does not impliment TCP_MAXSEG correctly. Fake it
// here. Netstack uses the max SACK size which is 32. The MSS
// option is 8 bytes, making the total 36 bytes.
pls = mss - 36
}
payload := make([]byte, pls)
seqNum1 := *conn.RemoteSeqNum()
const numPkts = 10
// Send some packets, checking that we receive each.
for i, sn := 0, seqNum1; i < numPkts; i++ {
dut.Send(acceptFd, payload, 0)
gotOne, err := conn.Expect(tb.TCP{SeqNum: tb.Uint32(uint32(sn))}, time.Second)
sn.UpdateForward(seqnum.Size(len(payload)))
if err != nil {
t.Errorf("Expect #%d: %s", i+1, err)
continue
}
if gotOne == nil {
t.Errorf("#%d: expected a packet within a second but got none", i+1)
}
}
seqNum2 := *conn.RemoteSeqNum()
// SACK packets #2-4.
sackBlock := make([]byte, 40)
sbOff := 0
sbOff += header.EncodeNOP(sackBlock[sbOff:])
sbOff += header.EncodeNOP(sackBlock[sbOff:])
sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
seqNum1.Add(seqnum.Size(len(payload))),
seqNum1.Add(seqnum.Size(4 * len(payload))),
}}, sackBlock[sbOff:])
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), AckNum: tb.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
// ACK first packet.
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), AckNum: tb.Uint32(uint32(seqNum1) + uint32(len(payload)))})
// Check for retransmit.
gotOne, err := conn.Expect(tb.TCP{SeqNum: tb.Uint32(uint32(seqNum1))}, time.Second)
if err != nil {
t.Error("Expect for retransmit:", err)
}
if gotOne == nil {
t.Error("expected a retransmitted packet within a second but got none")
}
// ACK all send packets with a DSACK block for packet #1. This tells
// the other end that we got both the original and retransmit for
// packet #1.
dsackBlock := make([]byte, 40)
dsbOff := 0
dsbOff += header.EncodeNOP(dsackBlock[dsbOff:])
dsbOff += header.EncodeNOP(dsackBlock[dsbOff:])
dsbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
seqNum1.Add(seqnum.Size(len(payload))),
seqNum1.Add(seqnum.Size(4 * len(payload))),
}}, dsackBlock[dsbOff:])
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), AckNum: tb.Uint32(uint32(seqNum2)), Options: dsackBlock[:dsbOff]})
// Send half of the original window of packets, checking that we
// received each.
for i, sn := 0, seqNum2; i < numPkts/2; i++ {
dut.Send(acceptFd, payload, 0)
gotOne, err := conn.Expect(tb.TCP{SeqNum: tb.Uint32(uint32(sn))}, time.Second)
sn.UpdateForward(seqnum.Size(len(payload)))
if err != nil {
t.Errorf("Expect #%d: %s", i+1, err)
continue
}
if gotOne == nil {
t.Errorf("#%d: expected a packet within a second but got none", i+1)
}
}
if tb.DUTType == "netstack" {
// The window should now be halved, so we should receive any
// more, even if we send them.
dut.Send(acceptFd, payload, 0)
if got, err := conn.Expect(tb.TCP{}, 100*time.Millisecond); got != nil || err == nil {
t.Fatalf("expected no packets within 100 millisecond, but got one: %s", got)
}
return
}
// Linux reduces the window by three. Check that we can receive the rest.
for i, sn := 0, seqNum2.Add(seqnum.Size(numPkts/2*len(payload))); i < 2; i++ {
dut.Send(acceptFd, payload, 0)
gotOne, err := conn.Expect(tb.TCP{SeqNum: tb.Uint32(uint32(sn))}, time.Second)
sn.UpdateForward(seqnum.Size(len(payload)))
if err != nil {
t.Errorf("Expect #%d: %s", i+1, err)
continue
}
if gotOne == nil {
t.Errorf("#%d: expected a packet within a second but got none", i+1)
}
}
// The window should now be full.
dut.Send(acceptFd, payload, 0)
if got, err := conn.Expect(tb.TCP{}, 100*time.Millisecond); got != nil || err == nil {
t.Fatalf("expected no packets within 100 millisecond, but got one: %s", got)
}
}