mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Handle TCP segment split cases as per MSS.
- Always split segments larger than MSS. Currently, we base the segment split decision as a function of the send congestion window and MSS, which could be greater than the MSS advertised by remote. - While splitting segments, ensure the PSH flag is reset when there are segments that are queued to be sent. - With TCP_CORK, hold up segments up until MSS. Fix a bug in computing available send space before attempting to coalesce segments. Fixes #2832 PiperOrigin-RevId: 314802928
This commit is contained in:
committed by
Nicolas Lacasse
parent
d61e88e342
commit
f766366091
@@ -618,6 +618,20 @@ func (s *sender) splitSeg(seg *segment, size int) {
|
||||
nSeg.data.TrimFront(size)
|
||||
nSeg.sequenceNumber.UpdateForward(seqnum.Size(size))
|
||||
s.writeList.InsertAfter(seg, nSeg)
|
||||
|
||||
// The segment being split does not carry PUSH flag because it is
|
||||
// followed by the newly split segment.
|
||||
// RFC1122 section 4.2.2.2: MUST set the PSH bit in the last buffered
|
||||
// segment (i.e., when there is no more queued data to be sent).
|
||||
// Linux removes PSH flag only when the segment is being split over MSS
|
||||
// and retains it when we are splitting the segment over lack of sender
|
||||
// window space.
|
||||
// ref: net/ipv4/tcp_output.c::tcp_write_xmit(), tcp_mss_split_point()
|
||||
// ref: net/ipv4/tcp_output.c::tcp_write_wakeup(), tcp_snd_wnd_test()
|
||||
if seg.data.Size() > s.maxPayloadSize {
|
||||
seg.flags ^= header.TCPFlagPsh
|
||||
}
|
||||
|
||||
seg.data.CapLength(size)
|
||||
}
|
||||
|
||||
@@ -739,7 +753,7 @@ func (s *sender) maybeSendSegment(seg *segment, limit int, end seqnum.Value) (se
|
||||
if !s.isAssignedSequenceNumber(seg) {
|
||||
// Merge segments if allowed.
|
||||
if seg.data.Size() != 0 {
|
||||
available := int(seg.sequenceNumber.Size(end))
|
||||
available := int(s.sndNxt.Size(end))
|
||||
if available > limit {
|
||||
available = limit
|
||||
}
|
||||
@@ -782,8 +796,11 @@ func (s *sender) maybeSendSegment(seg *segment, limit int, end seqnum.Value) (se
|
||||
// sent all at once.
|
||||
return false
|
||||
}
|
||||
if atomic.LoadUint32(&s.ep.cork) != 0 {
|
||||
// Hold back the segment until full.
|
||||
// With TCP_CORK, hold back until minimum of the available
|
||||
// send space and MSS.
|
||||
// TODO(gvisor.dev/issue/2833): Drain the held segments after a
|
||||
// timeout.
|
||||
if seg.data.Size() < s.maxPayloadSize && atomic.LoadUint32(&s.ep.cork) != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -843,9 +860,17 @@ func (s *sender) maybeSendSegment(seg *segment, limit int, end seqnum.Value) (se
|
||||
if available == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// The segment size limit is computed as a function of sender congestion
|
||||
// window and MSS. When sender congestion window is > 1, this limit can
|
||||
// be larger than MSS. Ensure that the currently available send space
|
||||
// is not greater than minimum of this limit and MSS.
|
||||
if available > limit {
|
||||
available = limit
|
||||
}
|
||||
if available > s.maxPayloadSize {
|
||||
available = s.maxPayloadSize
|
||||
}
|
||||
|
||||
if seg.data.Size() > available {
|
||||
s.splitSeg(seg, available)
|
||||
|
||||
@@ -628,15 +628,36 @@ func NewTCPIPv4(t *testing.T, outgoingTCP, incomingTCP TCP) TCPIPv4 {
|
||||
}
|
||||
}
|
||||
|
||||
// Handshake performs a TCP 3-way handshake. The input Connection should have a
|
||||
// Connect performs a TCP 3-way handshake. The input Connection should have a
|
||||
// final TCP Layer.
|
||||
func (conn *TCPIPv4) Handshake() {
|
||||
func (conn *TCPIPv4) Connect() {
|
||||
conn.t.Helper()
|
||||
|
||||
// Send the SYN.
|
||||
conn.Send(TCP{Flags: Uint8(header.TCPFlagSyn)})
|
||||
|
||||
// Wait for the SYN-ACK.
|
||||
synAck, err := conn.Expect(TCP{Flags: Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
|
||||
if synAck == nil {
|
||||
if err != nil {
|
||||
conn.t.Fatalf("didn't get synack during handshake: %s", err)
|
||||
}
|
||||
conn.layerStates[len(conn.layerStates)-1].(*tcpState).synAck = synAck
|
||||
|
||||
// Send an ACK.
|
||||
conn.Send(TCP{Flags: Uint8(header.TCPFlagAck)})
|
||||
}
|
||||
|
||||
// ConnectWithOptions performs a TCP 3-way handshake with given TCP options.
|
||||
// The input Connection should have a final TCP Layer.
|
||||
func (conn *TCPIPv4) ConnectWithOptions(options []byte) {
|
||||
conn.t.Helper()
|
||||
|
||||
// Send the SYN.
|
||||
conn.Send(TCP{Flags: Uint8(header.TCPFlagSyn), Options: options})
|
||||
|
||||
// Wait for the SYN-ACK.
|
||||
synAck, err := conn.Expect(TCP{Flags: Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
|
||||
if err != nil {
|
||||
conn.t.Fatalf("didn't get synack during handshake: %s", err)
|
||||
}
|
||||
conn.layerStates[len(conn.layerStates)-1].(*tcpState).synAck = synAck
|
||||
@@ -656,6 +677,31 @@ func (conn *TCPIPv4) ExpectData(tcp *TCP, payload *Payload, timeout time.Duratio
|
||||
return (*Connection)(conn).ExpectFrame(expected, timeout)
|
||||
}
|
||||
|
||||
// ExpectNextData attempts to receive the next incoming segment for the
|
||||
// connection and expects that to match the given layers.
|
||||
//
|
||||
// It differs from ExpectData() in that here we are only interested in the next
|
||||
// received segment, while ExpectData() can receive multiple segments for the
|
||||
// connection until there is a match with given layers or a timeout.
|
||||
func (conn *TCPIPv4) ExpectNextData(tcp *TCP, payload *Payload, timeout time.Duration) (Layers, error) {
|
||||
// Receive the first incoming TCP segment for this connection.
|
||||
got, err := conn.ExpectData(&TCP{}, nil, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expected := make([]Layer, len(conn.layerStates))
|
||||
expected[len(expected)-1] = tcp
|
||||
if payload != nil {
|
||||
expected = append(expected, payload)
|
||||
tcp.SeqNum = Uint32(uint32(*conn.RemoteSeqNum()) - uint32(payload.Length()))
|
||||
}
|
||||
if !(*Connection)(conn).match(expected, got) {
|
||||
return nil, fmt.Errorf("next frame is not matching %s during %s: got %s", expected, timeout, got)
|
||||
}
|
||||
return got, nil
|
||||
}
|
||||
|
||||
// Send a packet with reasonable defaults. Potentially override the TCP layer in
|
||||
// the connection with the provided layer and add additionLayers.
|
||||
func (conn *TCPIPv4) Send(tcp TCP, additionalLayers ...Layer) {
|
||||
|
||||
@@ -939,6 +939,11 @@ func (l *Payload) ToBytes() ([]byte, error) {
|
||||
return l.Bytes, nil
|
||||
}
|
||||
|
||||
// Length returns payload byte length.
|
||||
func (l *Payload) Length() int {
|
||||
return l.length()
|
||||
}
|
||||
|
||||
func (l *Payload) match(other Layer) bool {
|
||||
return equalLayer(l, other)
|
||||
}
|
||||
|
||||
@@ -199,6 +199,26 @@ packetimpact_go_test(
|
||||
],
|
||||
)
|
||||
|
||||
packetimpact_go_test(
|
||||
name = "tcp_splitseg_mss",
|
||||
srcs = ["tcp_splitseg_mss_test.go"],
|
||||
deps = [
|
||||
"//pkg/tcpip/header",
|
||||
"//test/packetimpact/testbench",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
packetimpact_go_test(
|
||||
name = "tcp_cork_mss",
|
||||
srcs = ["tcp_cork_mss_test.go"],
|
||||
deps = [
|
||||
"//pkg/tcpip/header",
|
||||
"//test/packetimpact/testbench",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
packetimpact_go_test(
|
||||
name = "icmpv6_param_problem",
|
||||
srcs = ["icmpv6_param_problem_test.go"],
|
||||
|
||||
@@ -21,11 +21,11 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
func TestFinWait2Timeout(t *testing.T) {
|
||||
@@ -37,13 +37,13 @@ func TestFinWait2Timeout(t *testing.T) {
|
||||
{"WithoutLinger2", false},
|
||||
} {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.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})
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
conn.Handshake()
|
||||
conn.Connect()
|
||||
|
||||
acceptFd, _ := dut.Accept(listenFd)
|
||||
if tt.linger2 {
|
||||
@@ -52,21 +52,21 @@ func TestFinWait2Timeout(t *testing.T) {
|
||||
}
|
||||
dut.Close(acceptFd)
|
||||
|
||||
if _, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
|
||||
if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
|
||||
t.Fatalf("expected a FIN-ACK within 1 second but got none: %s", err)
|
||||
}
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
conn.Drain()
|
||||
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
if tt.linger2 {
|
||||
if _, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
|
||||
if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
|
||||
t.Fatalf("expected a RST packet within a second but got none: %s", err)
|
||||
}
|
||||
} else {
|
||||
if got, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagRst)}, 10*time.Second); got != nil || err == nil {
|
||||
if got, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, 10*time.Second); got != nil || err == nil {
|
||||
t.Fatalf("expected no RST packets within ten seconds but got one: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,27 +21,27 @@ import (
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
// TestICMPv6ParamProblemTest sends a packet with a bad next header. The DUT
|
||||
// should respond with an ICMPv6 Parameter Problem message.
|
||||
func TestICMPv6ParamProblemTest(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.NewDUT(t)
|
||||
defer dut.TearDown()
|
||||
conn := tb.NewIPv6Conn(t, tb.IPv6{}, tb.IPv6{})
|
||||
conn := testbench.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{})
|
||||
defer conn.Close()
|
||||
ipv6 := tb.IPv6{
|
||||
ipv6 := testbench.IPv6{
|
||||
// 254 is reserved and used for experimentation and testing. This should
|
||||
// cause an error.
|
||||
NextHeader: tb.Uint8(254),
|
||||
NextHeader: testbench.Uint8(254),
|
||||
}
|
||||
icmpv6 := tb.ICMPv6{
|
||||
Type: tb.ICMPv6Type(header.ICMPv6EchoRequest),
|
||||
icmpv6 := testbench.ICMPv6{
|
||||
Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest),
|
||||
NDPPayload: []byte("hello world"),
|
||||
}
|
||||
|
||||
@@ -61,14 +61,14 @@ func TestICMPv6ParamProblemTest(t *testing.T) {
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(b, header.IPv6NextHeaderOffset)
|
||||
expectedPayload = append(b, expectedPayload...)
|
||||
expectedICMPv6 := tb.ICMPv6{
|
||||
Type: tb.ICMPv6Type(header.ICMPv6ParamProblem),
|
||||
expectedICMPv6 := testbench.ICMPv6{
|
||||
Type: testbench.ICMPv6Type(header.ICMPv6ParamProblem),
|
||||
NDPPayload: expectedPayload,
|
||||
}
|
||||
|
||||
paramProblem := tb.Layers{
|
||||
&tb.Ether{},
|
||||
&tb.IPv6{},
|
||||
paramProblem := testbench.Layers{
|
||||
&testbench.Ether{},
|
||||
&testbench.IPv6{},
|
||||
&expectedICMPv6,
|
||||
}
|
||||
timeout := time.Second
|
||||
|
||||
@@ -24,14 +24,14 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
func recvTCPSegment(conn *tb.TCPIPv4, expect *tb.TCP, expectPayload *tb.Payload) (uint16, error) {
|
||||
func recvTCPSegment(conn *testbench.TCPIPv4, expect *testbench.TCP, expectPayload *testbench.Payload) (uint16, error) {
|
||||
layers, err := conn.ExpectData(expect, expectPayload, time.Second)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to receive TCP segment: %s", err)
|
||||
@@ -39,7 +39,7 @@ func recvTCPSegment(conn *tb.TCPIPv4, expect *tb.TCP, expectPayload *tb.Payload)
|
||||
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].(*tb.IPv4)
|
||||
ipv4, ok := layers[1].(*testbench.IPv4)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("got network layer: %T, expected: *IPv4", layers[1])
|
||||
}
|
||||
@@ -56,16 +56,16 @@ func recvTCPSegment(conn *tb.TCPIPv4, expect *tb.TCP, expectPayload *tb.Payload)
|
||||
// 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) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.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})
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
conn.Handshake()
|
||||
conn.Connect()
|
||||
remoteFD, _ := dut.Accept(listenFD)
|
||||
defer dut.Close(remoteFD)
|
||||
|
||||
@@ -83,18 +83,18 @@ func TestIPv4RetransmitIdentificationUniqueness(t *testing.T) {
|
||||
}
|
||||
|
||||
sampleData := []byte("Sample Data")
|
||||
samplePayload := &tb.Payload{Bytes: sampleData}
|
||||
samplePayload := &testbench.Payload{Bytes: sampleData}
|
||||
|
||||
dut.Send(remoteFD, sampleData, 0)
|
||||
if _, err := conn.ExpectData(&tb.TCP{}, samplePayload, time.Second); err != nil {
|
||||
if _, err := conn.ExpectData(&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(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
|
||||
expectTCP := &tb.TCP{SeqNum: tb.Uint32(uint32(*conn.RemoteSeqNum()))}
|
||||
expectTCP := &testbench.TCP{SeqNum: testbench.Uint32(uint32(*conn.RemoteSeqNum()))}
|
||||
dut.Send(remoteFD, sampleData, 0)
|
||||
originalID, err := recvTCPSegment(&conn, expectTCP, samplePayload)
|
||||
if err != nil {
|
||||
|
||||
@@ -23,17 +23,17 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
func TestCloseWaitAck(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
description string
|
||||
makeTestingTCP func(conn *tb.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) tb.TCP
|
||||
makeTestingTCP func(conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP
|
||||
seqNumOffset seqnum.Size
|
||||
expectAck bool
|
||||
}{
|
||||
@@ -45,27 +45,27 @@ func TestCloseWaitAck(t *testing.T) {
|
||||
{"ACK", GenerateUnaccACKSegment, 2, true},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("%s%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.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})
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
conn.Handshake()
|
||||
conn.Connect()
|
||||
acceptFd, _ := dut.Accept(listenFd)
|
||||
|
||||
// Send a FIN to DUT to intiate the active close
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagFin)})
|
||||
gotTCP, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}, time.Second)
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagFin)})
|
||||
gotTCP, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("expected an ACK for our fin and DUT should enter CLOSE_WAIT: %s", err)
|
||||
}
|
||||
windowSize := seqnum.Size(*gotTCP.WindowSize)
|
||||
|
||||
// Send a segment with OTW Seq / unacc ACK and expect an ACK back
|
||||
conn.Send(tt.makeTestingTCP(&conn, tt.seqNumOffset, windowSize), &tb.Payload{Bytes: []byte("Sample Data")})
|
||||
gotAck, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}, time.Second)
|
||||
conn.Send(tt.makeTestingTCP(&conn, tt.seqNumOffset, windowSize), &testbench.Payload{Bytes: []byte("Sample Data")})
|
||||
gotAck, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
|
||||
if tt.expectAck && err != nil {
|
||||
t.Fatalf("expected an ack but got none: %s", err)
|
||||
}
|
||||
@@ -75,14 +75,14 @@ func TestCloseWaitAck(t *testing.T) {
|
||||
|
||||
// Now let's verify DUT is indeed in CLOSE_WAIT
|
||||
dut.Close(acceptFd)
|
||||
if _, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagFin)}, time.Second); err != nil {
|
||||
if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagFin)}, time.Second); err != nil {
|
||||
t.Fatalf("expected DUT to send a FIN: %s", err)
|
||||
}
|
||||
// Ack the FIN from DUT
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
// Send some extra data to DUT
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}, &tb.Payload{Bytes: []byte("Sample Data")})
|
||||
if _, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &testbench.Payload{Bytes: []byte("Sample Data")})
|
||||
if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
|
||||
t.Fatalf("expected DUT to send an RST: %s", err)
|
||||
}
|
||||
})
|
||||
@@ -92,17 +92,17 @@ func TestCloseWaitAck(t *testing.T) {
|
||||
// This generates an segment with seqnum = RCV.NXT + RCV.WND + seqNumOffset, the
|
||||
// generated segment is only acceptable when seqNumOffset is 0, otherwise an ACK
|
||||
// is expected from the receiver.
|
||||
func GenerateOTWSeqSegment(conn *tb.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) tb.TCP {
|
||||
func GenerateOTWSeqSegment(conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP {
|
||||
lastAcceptable := conn.LocalSeqNum().Add(windowSize)
|
||||
otwSeq := uint32(lastAcceptable.Add(seqNumOffset))
|
||||
return tb.TCP{SeqNum: tb.Uint32(otwSeq), Flags: tb.Uint8(header.TCPFlagAck)}
|
||||
return testbench.TCP{SeqNum: testbench.Uint32(otwSeq), Flags: testbench.Uint8(header.TCPFlagAck)}
|
||||
}
|
||||
|
||||
// This generates an segment with acknum = SND.NXT + seqNumOffset, the generated
|
||||
// segment is only acceptable when seqNumOffset is 0, otherwise an ACK is
|
||||
// expected from the receiver.
|
||||
func GenerateUnaccACKSegment(conn *tb.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) tb.TCP {
|
||||
func GenerateUnaccACKSegment(conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP {
|
||||
lastAcceptable := conn.RemoteSeqNum()
|
||||
unaccAck := uint32(lastAcceptable.Add(seqNumOffset))
|
||||
return tb.TCP{AckNum: tb.Uint32(unaccAck), Flags: tb.Uint8(header.TCPFlagAck)}
|
||||
return testbench.TCP{AckNum: testbench.Uint32(unaccAck), Flags: testbench.Uint8(header.TCPFlagAck)}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tcp_cork_mss_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
// TestTCPCorkMSS tests for segment coalesce and split as per MSS.
|
||||
func TestTCPCorkMSS(t *testing.T) {
|
||||
dut := testbench.NewDUT(t)
|
||||
defer dut.TearDown()
|
||||
listenFD, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
|
||||
defer dut.Close(listenFD)
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
const mss = uint32(header.TCPDefaultMSS)
|
||||
options := make([]byte, header.TCPOptionMSSLength)
|
||||
header.EncodeMSSOption(mss, options)
|
||||
conn.ConnectWithOptions(options)
|
||||
|
||||
acceptFD, _ := dut.Accept(listenFD)
|
||||
defer dut.Close(acceptFD)
|
||||
|
||||
dut.SetSockOptInt(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(acceptFD, sampleData, 0)
|
||||
dut.Send(acceptFD, sampleData, 0)
|
||||
|
||||
expectedData := sampleData
|
||||
expectedData = append(expectedData, sampleData...)
|
||||
largeData := make([]byte, mss+1)
|
||||
expectedData = append(expectedData, largeData...)
|
||||
dut.Send(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(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &expectedPayload, time.Second); err != nil {
|
||||
t.Fatalf("expected payload was not received: %s", err)
|
||||
}
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
// Expect the coalesced segment to be split and transmitted.
|
||||
expectedPayload = testbench.Payload{Bytes: expectedData[mss:]}
|
||||
if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &expectedPayload, time.Second); err != nil {
|
||||
t.Fatalf("expected payload was not received: %s", err)
|
||||
}
|
||||
|
||||
// Check for segments to *not* be held up because of TCP_CORK when
|
||||
// the current send window is less than MSS.
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(uint16(2 * len(sampleData)))})
|
||||
dut.Send(acceptFD, sampleData, 0)
|
||||
dut.Send(acceptFD, sampleData, 0)
|
||||
expectedPayload = testbench.Payload{Bytes: append(sampleData, sampleData...)}
|
||||
if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &expectedPayload, time.Second); err != nil {
|
||||
t.Fatalf("expected payload was not received: %s", err)
|
||||
}
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
}
|
||||
@@ -21,22 +21,22 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
func TestTcpNoAcceptCloseReset(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.NewDUT(t)
|
||||
defer dut.TearDown()
|
||||
listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
|
||||
conn := tb.NewTCPIPv4(t, tb.TCP{DstPort: &remotePort}, tb.TCP{SrcPort: &remotePort})
|
||||
conn.Handshake()
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
conn.Connect()
|
||||
defer conn.Close()
|
||||
dut.Close(listenFd)
|
||||
if _, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagRst | header.TCPFlagAck)}, 1*time.Second); err != nil {
|
||||
if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)}, 1*time.Second); err != nil {
|
||||
t.Fatalf("expected a RST-ACK packet but got none: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
// TestTCPOutsideTheWindows tests the behavior of the DUT when packets arrive
|
||||
@@ -38,7 +38,7 @@ func TestTCPOutsideTheWindow(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
description string
|
||||
tcpFlags uint8
|
||||
payload []tb.Layer
|
||||
payload []testbench.Layer
|
||||
seqNumOffset seqnum.Size
|
||||
expectACK bool
|
||||
}{
|
||||
@@ -46,28 +46,28 @@ func TestTCPOutsideTheWindow(t *testing.T) {
|
||||
{"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 0, true},
|
||||
{"ACK", header.TCPFlagAck, nil, 0, false},
|
||||
{"FIN", header.TCPFlagFin, nil, 0, false},
|
||||
{"Data", header.TCPFlagAck, []tb.Layer{&tb.Payload{Bytes: []byte("abc123")}}, 0, true},
|
||||
{"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, []tb.Layer{&tb.Payload{Bytes: []byte("abc123")}}, 1, true},
|
||||
{"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, []tb.Layer{&tb.Payload{Bytes: []byte("abc123")}}, 2, true},
|
||||
{"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 := tb.NewDUT(t)
|
||||
dut := testbench.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})
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
conn.Handshake()
|
||||
conn.Connect()
|
||||
acceptFD, _ := dut.Accept(listenFD)
|
||||
defer dut.Close(acceptFD)
|
||||
|
||||
@@ -75,13 +75,13 @@ func TestTCPOutsideTheWindow(t *testing.T) {
|
||||
conn.Drain()
|
||||
// Ignore whatever incrementing that this out-of-order packet might cause
|
||||
// to the AckNum.
|
||||
localSeqNum := tb.Uint32(uint32(*conn.LocalSeqNum()))
|
||||
conn.Send(tb.TCP{
|
||||
Flags: tb.Uint8(tt.tcpFlags),
|
||||
SeqNum: tb.Uint32(uint32(conn.LocalSeqNum().Add(windowSize))),
|
||||
localSeqNum := testbench.Uint32(uint32(*conn.LocalSeqNum()))
|
||||
conn.Send(testbench.TCP{
|
||||
Flags: testbench.Uint8(tt.tcpFlags),
|
||||
SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum().Add(windowSize))),
|
||||
}, tt.payload...)
|
||||
timeout := 3 * time.Second
|
||||
gotACK, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), AckNum: localSeqNum}, timeout)
|
||||
gotACK, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: localSeqNum}, timeout)
|
||||
if tt.expectACK && err != nil {
|
||||
t.Fatalf("expected an ACK packet within %s but got none: %s", timeout, err)
|
||||
}
|
||||
|
||||
@@ -22,25 +22,25 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
func TestPAWSMechanism(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.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})
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
options := make([]byte, header.TCPOptionTSLength)
|
||||
header.EncodeTSOption(currentTS(), 0, options)
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn), Options: options})
|
||||
synAck, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn), Options: options})
|
||||
synAck, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("didn't get synack during handshake: %s", err)
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func TestPAWSMechanism(t *testing.T) {
|
||||
}
|
||||
tsecr := parsedSynOpts.TSVal
|
||||
header.EncodeTSOption(currentTS(), tsecr, options)
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), Options: options})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), Options: options})
|
||||
acceptFD, _ := dut.Accept(listenFD)
|
||||
defer dut.Close(acceptFD)
|
||||
|
||||
@@ -61,9 +61,9 @@ func TestPAWSMechanism(t *testing.T) {
|
||||
// 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(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), Options: options}, &tb.Payload{Bytes: sampleData})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
|
||||
|
||||
gotTCP, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}, time.Second)
|
||||
gotTCP, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("expected an ACK but got none: %s", err)
|
||||
}
|
||||
@@ -86,9 +86,9 @@ func TestPAWSMechanism(t *testing.T) {
|
||||
// 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(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), Options: options}, &tb.Payload{Bytes: sampleData})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
|
||||
|
||||
gotTCP, err = conn.Expect(tb.TCP{AckNum: lastAckNum, Flags: tb.Uint8(header.TCPFlagAck)}, time.Second)
|
||||
gotTCP, err = conn.Expect(testbench.TCP{AckNum: lastAckNum, Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("expected segment with AckNum %d but got none: %s", lastAckNum, err)
|
||||
}
|
||||
|
||||
@@ -28,19 +28,19 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
func TestQueueReceiveInSynSent(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.NewDUT(t)
|
||||
defer dut.TearDown()
|
||||
|
||||
socket, remotePort := dut.CreateBoundSocket(unix.SOCK_STREAM, unix.IPPROTO_TCP, net.ParseIP(tb.RemoteIPv4))
|
||||
conn := tb.NewTCPIPv4(t, tb.TCP{DstPort: &remotePort}, tb.TCP{SrcPort: &remotePort})
|
||||
socket, remotePort := dut.CreateBoundSocket(unix.SOCK_STREAM, unix.IPPROTO_TCP, net.ParseIP(testbench.RemoteIPv4))
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
sampleData := []byte("Sample Data")
|
||||
@@ -49,7 +49,7 @@ func TestQueueReceiveInSynSent(t *testing.T) {
|
||||
if _, err := dut.ConnectWithErrno(context.Background(), socket, conn.LocalAddr()); !errors.Is(err, syscall.EINPROGRESS) {
|
||||
t.Fatalf("failed to bring DUT to SYN-SENT, got: %s, want EINPROGRESS", err)
|
||||
}
|
||||
if _, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn)}, time.Second); err != nil {
|
||||
if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)}, time.Second); err != nil {
|
||||
t.Fatalf("expected a SYN from DUT, but got none: %s", err)
|
||||
}
|
||||
|
||||
@@ -77,11 +77,11 @@ func TestQueueReceiveInSynSent(t *testing.T) {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// Bring the connection to Established.
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn | header.TCPFlagAck)})
|
||||
if _, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)})
|
||||
if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
|
||||
t.Fatalf("expected an ACK from DUT, but got none: %s", err)
|
||||
}
|
||||
|
||||
// Send sample data to DUT.
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}, &tb.Payload{Bytes: sampleData})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData})
|
||||
}
|
||||
|
||||
@@ -21,53 +21,53 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
// TestRetransmits tests retransmits occur at exponentially increasing
|
||||
// time intervals.
|
||||
func TestRetransmits(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.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})
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
conn.Handshake()
|
||||
conn.Connect()
|
||||
acceptFd, _ := dut.Accept(listenFd)
|
||||
defer dut.Close(acceptFd)
|
||||
|
||||
dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
|
||||
|
||||
sampleData := []byte("Sample Data")
|
||||
samplePayload := &tb.Payload{Bytes: sampleData}
|
||||
samplePayload := &testbench.Payload{Bytes: sampleData}
|
||||
|
||||
dut.Send(acceptFd, sampleData, 0)
|
||||
if _, err := conn.ExpectData(&tb.TCP{}, samplePayload, time.Second); err != nil {
|
||||
if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
|
||||
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
|
||||
}
|
||||
// Give a chance for the dut to estimate RTO with RTT from the DATA-ACK.
|
||||
// TODO(gvisor.dev/issue/2685) Estimate RTO during handshake, after which
|
||||
// we can skip sending this ACK.
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
|
||||
startRTO := time.Second
|
||||
current := startRTO
|
||||
first := time.Now()
|
||||
dut.Send(acceptFd, sampleData, 0)
|
||||
seq := tb.Uint32(uint32(*conn.RemoteSeqNum()))
|
||||
if _, err := conn.ExpectData(&tb.TCP{SeqNum: seq}, samplePayload, startRTO); err != nil {
|
||||
seq := testbench.Uint32(uint32(*conn.RemoteSeqNum()))
|
||||
if _, err := conn.ExpectData(&testbench.TCP{SeqNum: seq}, samplePayload, startRTO); err != nil {
|
||||
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
|
||||
}
|
||||
// Expect retransmits of the same segment.
|
||||
for i := 0; i < 5; i++ {
|
||||
start := time.Now()
|
||||
if _, err := conn.ExpectData(&tb.TCP{SeqNum: seq}, samplePayload, 2*current); err != nil {
|
||||
if _, err := conn.ExpectData(&testbench.TCP{SeqNum: seq}, samplePayload, 2*current); err != nil {
|
||||
t.Fatalf("expected a packet with payload %v: %s loop %d", samplePayload, err, i)
|
||||
}
|
||||
if i == 0 {
|
||||
|
||||
@@ -22,11 +22,11 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
// TestSendWindowSizesPiggyback tests cases where segment sizes are close to
|
||||
@@ -59,26 +59,26 @@ func TestSendWindowSizesPiggyback(t *testing.T) {
|
||||
{"WindowGreaterThanSegment", segmentSize + 1, sampleData, sampleData, true /* enqueue */},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("%s%d", tt.description, tt.windowSize), func(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.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, WindowSize: tb.Uint16(tt.windowSize)}, tb.TCP{SrcPort: &remotePort})
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort, WindowSize: testbench.Uint16(tt.windowSize)}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
conn.Handshake()
|
||||
conn.Connect()
|
||||
acceptFd, _ := dut.Accept(listenFd)
|
||||
defer dut.Close(acceptFd)
|
||||
|
||||
dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
|
||||
|
||||
expectedTCP := tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}
|
||||
expectedTCP := testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}
|
||||
|
||||
dut.Send(acceptFd, sampleData, 0)
|
||||
expectedPayload := tb.Payload{Bytes: tt.expectedPayload1}
|
||||
expectedPayload := testbench.Payload{Bytes: tt.expectedPayload1}
|
||||
if _, err := conn.ExpectData(&expectedTCP, &expectedPayload, time.Second); err != nil {
|
||||
t.Fatalf("Expected %s but didn't get one: %s", tb.Layers{&expectedTCP, &expectedPayload}, err)
|
||||
t.Fatalf("expected payload was not received: %s", err)
|
||||
}
|
||||
|
||||
// Expect any enqueued segment to be transmitted by the dut along with
|
||||
@@ -92,13 +92,13 @@ func TestSendWindowSizesPiggyback(t *testing.T) {
|
||||
// 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(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagPsh), WindowSize: tb.Uint16(tt.windowSize)}, &tb.Payload{Bytes: sampleData})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh), WindowSize: testbench.Uint16(tt.windowSize)}, &testbench.Payload{Bytes: sampleData})
|
||||
|
||||
// Expect the dut to piggyback the ACK for received data along with
|
||||
// the segment enqueued for transmit.
|
||||
expectedPayload = tb.Payload{Bytes: tt.expectedPayload2}
|
||||
expectedPayload = testbench.Payload{Bytes: tt.expectedPayload2}
|
||||
if _, err := conn.ExpectData(&expectedTCP, &expectedPayload, time.Second); err != nil {
|
||||
t.Fatalf("Expected %s but didn't get one: %s", tb.Layers{&expectedTCP, &expectedPayload}, err)
|
||||
t.Fatalf("expected payload was not received: %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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_splitseg_mss_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
// TestTCPSplitSegMSS lets the dut try to send segments larger than MSS.
|
||||
// It tests if the transmitted segments are capped at MSS and are split.
|
||||
func TestTCPSplitSegMSS(t *testing.T) {
|
||||
dut := testbench.NewDUT(t)
|
||||
defer dut.TearDown()
|
||||
listenFD, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
|
||||
defer dut.Close(listenFD)
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
const mss = uint32(header.TCPDefaultMSS)
|
||||
options := make([]byte, header.TCPOptionMSSLength)
|
||||
header.EncodeMSSOption(mss, options)
|
||||
conn.ConnectWithOptions(options)
|
||||
|
||||
acceptFD, _ := dut.Accept(listenFD)
|
||||
defer dut.Close(acceptFD)
|
||||
|
||||
// Let the dut send a segment larger than MSS.
|
||||
largeData := make([]byte, mss+1)
|
||||
for i := 0; i < 2; i++ {
|
||||
dut.Send(acceptFD, largeData, 0)
|
||||
if i == 0 {
|
||||
// On Linux, the initial segment goes out beyond MSS and the segment
|
||||
// split occurs on retransmission. Call ExpectData to wait to
|
||||
// receive the split segment.
|
||||
if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &testbench.Payload{Bytes: largeData[:mss]}, time.Second); err != nil {
|
||||
t.Fatalf("expected payload was not received: %s", err)
|
||||
}
|
||||
} else {
|
||||
if _, err := conn.ExpectNextData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &testbench.Payload{Bytes: largeData[:mss]}, time.Second); err != nil {
|
||||
t.Fatalf("expected payload was not received: %s", err)
|
||||
}
|
||||
}
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
if _, err := conn.ExpectNextData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &testbench.Payload{Bytes: largeData[mss:]}, time.Second); err != nil {
|
||||
t.Fatalf("expected payload was not received: %s", err)
|
||||
}
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
}
|
||||
}
|
||||
@@ -21,32 +21,32 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
// TestTCPSynRcvdReset tests transition from SYN-RCVD to CLOSED.
|
||||
func TestTCPSynRcvdReset(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.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})
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
// Expect dut connection to have transitioned to SYN-RCVD state.
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn)})
|
||||
if _, err := conn.ExpectData(&tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)})
|
||||
if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
|
||||
t.Fatalf("expected SYN-ACK %s", err)
|
||||
}
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagRst)})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)})
|
||||
// Expect the connection to have transitioned SYN-RCVD to CLOSED.
|
||||
// TODO(gvisor.dev/issue/478): Check for TCP_INFO on the dut side.
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
|
||||
if _, err := conn.ExpectData(&tb.TCP{Flags: tb.Uint8(header.TCPFlagRst)}, nil, time.Second); err != nil {
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, nil, time.Second); err != nil {
|
||||
t.Fatalf("expected a TCP RST %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,27 +22,27 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
func sendPayload(conn *tb.TCPIPv4, dut *tb.DUT, fd int32) error {
|
||||
func sendPayload(conn *testbench.TCPIPv4, dut *testbench.DUT, fd int32) error {
|
||||
sampleData := make([]byte, 100)
|
||||
for i := range sampleData {
|
||||
sampleData[i] = uint8(i)
|
||||
}
|
||||
conn.Drain()
|
||||
dut.Send(fd, sampleData, 0)
|
||||
if _, err := conn.ExpectData(&tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &tb.Payload{Bytes: sampleData}, time.Second); err != nil {
|
||||
if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &testbench.Payload{Bytes: sampleData}, time.Second); err != nil {
|
||||
return fmt.Errorf("expected data but got none: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendFIN(conn *tb.TCPIPv4, dut *tb.DUT, fd int32) error {
|
||||
func sendFIN(conn *testbench.TCPIPv4, dut *testbench.DUT, fd int32) error {
|
||||
dut.Close(fd)
|
||||
return nil
|
||||
}
|
||||
@@ -59,20 +59,20 @@ func TestTCPUserTimeout(t *testing.T) {
|
||||
} {
|
||||
for _, ttf := range []struct {
|
||||
description string
|
||||
f func(conn *tb.TCPIPv4, dut *tb.DUT, fd int32) error
|
||||
f func(conn *testbench.TCPIPv4, dut *testbench.DUT, fd int32) error
|
||||
}{
|
||||
{"AfterPayload", sendPayload},
|
||||
{"AfterFIN", sendFIN},
|
||||
} {
|
||||
t.Run(tt.description+ttf.description, func(t *testing.T) {
|
||||
// Create a socket, listen, TCP handshake, and accept.
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.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})
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
conn.Handshake()
|
||||
conn.Connect()
|
||||
acceptFD, _ := dut.Accept(listenFD)
|
||||
|
||||
if tt.userTimeout != 0 {
|
||||
@@ -85,14 +85,14 @@ func TestTCPUserTimeout(t *testing.T) {
|
||||
|
||||
time.Sleep(tt.sendDelay)
|
||||
conn.Drain()
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
|
||||
// If TCP_USER_TIMEOUT was set and the above delay was longer than the
|
||||
// TCP_USER_TIMEOUT then the DUT should send a RST in response to the
|
||||
// testbench's packet.
|
||||
expectRST := tt.userTimeout != 0 && tt.sendDelay > tt.userTimeout
|
||||
expectTimeout := 5 * time.Second
|
||||
got, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagRst)}, expectTimeout)
|
||||
got, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, expectTimeout)
|
||||
if expectRST && err != nil {
|
||||
t.Errorf("expected RST packet within %s but got none: %s", expectTimeout, err)
|
||||
}
|
||||
|
||||
@@ -21,53 +21,53 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
func TestWindowShrink(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.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})
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
conn.Handshake()
|
||||
conn.Connect()
|
||||
acceptFd, _ := dut.Accept(listenFd)
|
||||
defer dut.Close(acceptFd)
|
||||
|
||||
dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
|
||||
|
||||
sampleData := []byte("Sample Data")
|
||||
samplePayload := &tb.Payload{Bytes: sampleData}
|
||||
samplePayload := &testbench.Payload{Bytes: sampleData}
|
||||
|
||||
dut.Send(acceptFd, sampleData, 0)
|
||||
if _, err := conn.ExpectData(&tb.TCP{}, samplePayload, time.Second); err != nil {
|
||||
if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
|
||||
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
|
||||
}
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
|
||||
dut.Send(acceptFd, sampleData, 0)
|
||||
dut.Send(acceptFd, sampleData, 0)
|
||||
if _, err := conn.ExpectData(&tb.TCP{}, samplePayload, time.Second); err != nil {
|
||||
if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
|
||||
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
|
||||
}
|
||||
if _, err := conn.ExpectData(&tb.TCP{}, samplePayload, time.Second); err != nil {
|
||||
if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
|
||||
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
|
||||
}
|
||||
// We close our receiving window here
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), WindowSize: tb.Uint16(0)})
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
|
||||
|
||||
dut.Send(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() - 1
|
||||
if _, err := conn.ExpectData(&tb.TCP{SeqNum: tb.Uint32(uint32(expectedRemoteSeqNum))}, nil, time.Second); err != nil {
|
||||
if _, err := conn.ExpectData(&testbench.TCP{SeqNum: testbench.Uint32(uint32(expectedRemoteSeqNum))}, nil, time.Second); err != nil {
|
||||
t.Fatalf("expected a packet with sequence number %v: %s", expectedRemoteSeqNum, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,39 +21,39 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tb.RegisterFlags(flag.CommandLine)
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
// TestZeroWindowProbeRetransmit tests retransmits of zero window probes
|
||||
// to be sent at exponentially inreasing time intervals.
|
||||
func TestZeroWindowProbeRetransmit(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
dut := testbench.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})
|
||||
conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
conn.Handshake()
|
||||
conn.Connect()
|
||||
acceptFd, _ := dut.Accept(listenFd)
|
||||
defer dut.Close(acceptFd)
|
||||
|
||||
dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
|
||||
|
||||
sampleData := []byte("Sample Data")
|
||||
samplePayload := &tb.Payload{Bytes: sampleData}
|
||||
samplePayload := &testbench.Payload{Bytes: sampleData}
|
||||
|
||||
// Send and receive sample data to the dut.
|
||||
dut.Send(acceptFd, sampleData, 0)
|
||||
if _, err := conn.ExpectData(&tb.TCP{}, samplePayload, time.Second); err != nil {
|
||||
if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
|
||||
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
|
||||
}
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
|
||||
if _, err := conn.ExpectData(&tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}, nil, time.Second); err != nil {
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
|
||||
if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, nil, time.Second); err != nil {
|
||||
t.Fatalf("expected a packet with sequence number %s", err)
|
||||
}
|
||||
|
||||
@@ -63,9 +63,9 @@ func TestZeroWindowProbeRetransmit(t *testing.T) {
|
||||
// of the recorded first zero probe transmission duration.
|
||||
//
|
||||
// Advertize zero receive window again.
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), WindowSize: tb.Uint16(0)})
|
||||
probeSeq := tb.Uint32(uint32(*conn.RemoteSeqNum() - 1))
|
||||
ackProbe := tb.Uint32(uint32(*conn.RemoteSeqNum()))
|
||||
conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
|
||||
probeSeq := testbench.Uint32(uint32(*conn.RemoteSeqNum() - 1))
|
||||
ackProbe := testbench.Uint32(uint32(*conn.RemoteSeqNum()))
|
||||
|
||||
startProbeDuration := time.Second
|
||||
current := startProbeDuration
|
||||
@@ -79,7 +79,7 @@ func TestZeroWindowProbeRetransmit(t *testing.T) {
|
||||
// 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(&tb.TCP{SeqNum: probeSeq}, nil, 2*current); err != nil {
|
||||
if _, err := conn.ExpectData(&testbench.TCP{SeqNum: probeSeq}, nil, 2*current); err != nil {
|
||||
t.Fatalf("expected a probe with sequence number %v: loop %d", probeSeq, i)
|
||||
}
|
||||
if i == 0 {
|
||||
@@ -92,13 +92,14 @@ func TestZeroWindowProbeRetransmit(t *testing.T) {
|
||||
t.Fatalf("zero probe came sooner interval %d probe %d\n", p, i)
|
||||
}
|
||||
// Acknowledge the zero-window probes from the dut.
|
||||
conn.Send(tb.TCP{AckNum: ackProbe, Flags: tb.Uint8(header.TCPFlagAck), WindowSize: tb.Uint16(0)})
|
||||
conn.Send(testbench.TCP{AckNum: ackProbe, Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
|
||||
current *= 2
|
||||
}
|
||||
// Advertize non-zero window.
|
||||
conn.Send(tb.TCP{AckNum: ackProbe, Flags: tb.Uint8(header.TCPFlagAck)})
|
||||
conn.Send(testbench.TCP{AckNum: ackProbe, Flags: testbench.Uint8(header.TCPFlagAck)})
|
||||
// Expect the dut to recover and transmit data.
|
||||
if _, err := conn.ExpectData(&tb.TCP{SeqNum: ackProbe}, samplePayload, time.Second); err != nil {
|
||||
if _, err := conn.ExpectData(&testbench.
|
||||
TCP{SeqNum: ackProbe}, samplePayload, time.Second); err != nil {
|
||||
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user