github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/packetimpact/tests/tcp_rack_test.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tcp_rack_test
    16  
    17  import (
    18  	"flag"
    19  	"testing"
    20  	"time"
    21  
    22  	"golang.org/x/sys/unix"
    23  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    24  	"github.com/SagerNet/gvisor/pkg/tcpip/header"
    25  	"github.com/SagerNet/gvisor/pkg/tcpip/seqnum"
    26  	"github.com/SagerNet/gvisor/test/packetimpact/testbench"
    27  )
    28  
    29  func init() {
    30  	testbench.Initialize(flag.CommandLine)
    31  }
    32  
    33  const (
    34  	// payloadSize is the size used to send packets.
    35  	payloadSize = header.TCPDefaultMSS
    36  
    37  	// simulatedRTT is the time delay between packets sent and acked to
    38  	// increase the RTT.
    39  	simulatedRTT = 30 * time.Millisecond
    40  
    41  	// numPktsForRTT is the number of packets sent and acked to establish
    42  	// RTT.
    43  	numPktsForRTT = 10
    44  )
    45  
    46  func createSACKConnection(t *testing.T) (testbench.DUT, testbench.TCPIPv4, int32, int32) {
    47  	dut := testbench.NewDUT(t)
    48  	listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
    49  	conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
    50  
    51  	// Enable SACK.
    52  	opts := make([]byte, 40)
    53  	optsOff := 0
    54  	optsOff += header.EncodeNOP(opts[optsOff:])
    55  	optsOff += header.EncodeNOP(opts[optsOff:])
    56  	optsOff += header.EncodeSACKPermittedOption(opts[optsOff:])
    57  
    58  	conn.ConnectWithOptions(t, opts[:optsOff])
    59  	acceptFd, _ := dut.Accept(t, listenFd)
    60  	return dut, conn, acceptFd, listenFd
    61  }
    62  
    63  func closeSACKConnection(t *testing.T, dut testbench.DUT, conn testbench.TCPIPv4, acceptFd, listenFd int32) {
    64  	dut.Close(t, acceptFd)
    65  	dut.Close(t, listenFd)
    66  	conn.Close(t)
    67  }
    68  
    69  func getRTTAndRTO(t *testing.T, dut testbench.DUT, acceptFd int32) (rtt, rto time.Duration) {
    70  	info := dut.GetSockOptTCPInfo(t, acceptFd)
    71  	return time.Duration(info.RTT) * time.Microsecond, time.Duration(info.RTO) * time.Microsecond
    72  }
    73  
    74  func sendAndReceive(t *testing.T, dut testbench.DUT, conn testbench.TCPIPv4, numPkts int, acceptFd int32, sendACK bool) time.Time {
    75  	seqNum1 := *conn.RemoteSeqNum(t)
    76  	payload := make([]byte, payloadSize)
    77  	var lastSent time.Time
    78  	for i, sn := 0, seqNum1; i < numPkts; i++ {
    79  		lastSent = time.Now()
    80  		dut.Send(t, acceptFd, payload, 0)
    81  		gotOne, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(sn))}, time.Second)
    82  		if err != nil {
    83  			t.Fatalf("Expect #%d: %s", i+1, err)
    84  			continue
    85  		}
    86  		if gotOne == nil {
    87  			t.Fatalf("#%d: expected a packet within a second but got none", i+1)
    88  		}
    89  		sn.UpdateForward(seqnum.Size(payloadSize))
    90  
    91  		if sendACK {
    92  			time.Sleep(simulatedRTT)
    93  			conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(sn))})
    94  		}
    95  	}
    96  	return lastSent
    97  }
    98  
    99  // TestRACKTLPAllPacketsLost tests TLP when an entire flight of data is lost.
   100  func TestRACKTLPAllPacketsLost(t *testing.T) {
   101  	dut, conn, acceptFd, listenFd := createSACKConnection(t)
   102  	seqNum1 := *conn.RemoteSeqNum(t)
   103  
   104  	// Send ACK for data packets to establish RTT.
   105  	sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
   106  	seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
   107  
   108  	// We are not sending ACK for these packets.
   109  	const numPkts = 5
   110  	lastSent := sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
   111  
   112  	// Probe Timeout (PTO) should be two times RTT. Check that the last
   113  	// packet is retransmitted after probe timeout.
   114  	rtt, _ := getRTTAndRTO(t, dut, acceptFd)
   115  	pto := rtt * 2
   116  	// We expect the 5th packet (the last unacknowledged packet) to be
   117  	// retransmitted.
   118  	tlpProbe := testbench.Uint32(uint32(seqNum1) + uint32((numPkts-1)*payloadSize))
   119  	if _, err := conn.Expect(t, testbench.TCP{SeqNum: tlpProbe}, time.Second); err != nil {
   120  		t.Fatalf("expected payload was not received: %s %v %v", err, rtt, pto)
   121  	}
   122  	diff := time.Now().Sub(lastSent)
   123  	if diff < pto {
   124  		t.Fatalf("expected payload was received before the probe timeout, got: %v, want: %v", diff, pto)
   125  	}
   126  	closeSACKConnection(t, dut, conn, acceptFd, listenFd)
   127  }
   128  
   129  // TestRACKTLPLost tests TLP when there are tail losses.
   130  // See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.4
   131  func TestRACKTLPLost(t *testing.T) {
   132  	dut, conn, acceptFd, listenFd := createSACKConnection(t)
   133  	seqNum1 := *conn.RemoteSeqNum(t)
   134  
   135  	// Send ACK for data packets to establish RTT.
   136  	sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
   137  	seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
   138  
   139  	// We are not sending ACK for these packets.
   140  	const numPkts = 10
   141  	lastSent := sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
   142  
   143  	// Cumulative ACK for #[1-5] packets.
   144  	ackNum := seqNum1.Add(seqnum.Size(6 * payloadSize))
   145  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(ackNum))})
   146  
   147  	// Probe Timeout (PTO) should be two times RTT. Check that the last
   148  	// packet is retransmitted after probe timeout.
   149  	rtt, _ := getRTTAndRTO(t, dut, acceptFd)
   150  	pto := rtt * 2
   151  	// We expect the 10th packet (the last unacknowledged packet) to be
   152  	// retransmitted.
   153  	tlpProbe := testbench.Uint32(uint32(seqNum1) + uint32((numPkts-1)*payloadSize))
   154  	if _, err := conn.Expect(t, testbench.TCP{SeqNum: tlpProbe}, time.Second); err != nil {
   155  		t.Fatalf("expected payload was not received: %s", err)
   156  	}
   157  	diff := time.Now().Sub(lastSent)
   158  	if diff < pto {
   159  		t.Fatalf("expected payload was received before the probe timeout, got: %v, want: %v", diff, pto)
   160  	}
   161  	closeSACKConnection(t, dut, conn, acceptFd, listenFd)
   162  }
   163  
   164  // TestRACKWithSACK tests that RACK marks the packets as lost after receiving
   165  // the ACK for retransmitted packets.
   166  // See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-8.1
   167  func TestRACKWithSACK(t *testing.T) {
   168  	dut, conn, acceptFd, listenFd := createSACKConnection(t)
   169  	seqNum1 := *conn.RemoteSeqNum(t)
   170  
   171  	// Send ACK for data packets to establish RTT.
   172  	sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
   173  	seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
   174  
   175  	// We are not sending ACK for these packets.
   176  	const numPkts = 3
   177  	sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
   178  
   179  	time.Sleep(simulatedRTT)
   180  	// SACK for #2 packet.
   181  	sackBlock := make([]byte, 40)
   182  	start := seqNum1.Add(seqnum.Size(payloadSize))
   183  	end := start.Add(seqnum.Size(payloadSize))
   184  	sbOff := 0
   185  	sbOff += header.EncodeNOP(sackBlock[sbOff:])
   186  	sbOff += header.EncodeNOP(sackBlock[sbOff:])
   187  	sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
   188  		start, end,
   189  	}}, sackBlock[sbOff:])
   190  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
   191  
   192  	rtt, _ := getRTTAndRTO(t, dut, acceptFd)
   193  	timeout := 2 * rtt
   194  	// RACK marks #1 packet as lost after RTT+reorderWindow(RTT/4) and
   195  	// retransmits it.
   196  	if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, timeout); err != nil {
   197  		t.Fatalf("expected payload was not received: %s", err)
   198  	}
   199  
   200  	time.Sleep(simulatedRTT)
   201  	// ACK for #1 packet.
   202  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(end))})
   203  
   204  	// RACK considers transmission times of the packets to mark them lost.
   205  	// As the 3rd packet was sent before the retransmitted 1st packet, RACK
   206  	// marks it as lost and retransmits it..
   207  	expectedSeqNum := testbench.Uint32(uint32(seqNum1) + uint32((numPkts-1)*payloadSize))
   208  	if _, err := conn.Expect(t, testbench.TCP{SeqNum: expectedSeqNum}, timeout); err != nil {
   209  		t.Fatalf("expected payload was not received: %s", err)
   210  	}
   211  	closeSACKConnection(t, dut, conn, acceptFd, listenFd)
   212  }
   213  
   214  // TestRACKWithoutReorder tests that without reordering RACK will retransmit the
   215  // lost packets after reorder timer expires.
   216  func TestRACKWithoutReorder(t *testing.T) {
   217  	dut, conn, acceptFd, listenFd := createSACKConnection(t)
   218  	seqNum1 := *conn.RemoteSeqNum(t)
   219  
   220  	// Send ACK for data packets to establish RTT.
   221  	sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
   222  	seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
   223  
   224  	// We are not sending ACK for these packets.
   225  	const numPkts = 4
   226  	sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
   227  
   228  	// SACK for [3,4] packets.
   229  	sackBlock := make([]byte, 40)
   230  	start := seqNum1.Add(seqnum.Size(2 * payloadSize))
   231  	end := start.Add(seqnum.Size(2 * payloadSize))
   232  	sbOff := 0
   233  	sbOff += header.EncodeNOP(sackBlock[sbOff:])
   234  	sbOff += header.EncodeNOP(sackBlock[sbOff:])
   235  	sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
   236  		start, end,
   237  	}}, sackBlock[sbOff:])
   238  	time.Sleep(simulatedRTT)
   239  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
   240  
   241  	// RACK marks #1 and #2 packets as lost and retransmits both after
   242  	// RTT + reorderWindow. The reorderWindow initially will be a small
   243  	// fraction of RTT.
   244  	rtt, _ := getRTTAndRTO(t, dut, acceptFd)
   245  	timeout := 2 * rtt
   246  	for i, sn := 0, seqNum1; i < 2; i++ {
   247  		if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(sn))}, timeout); err != nil {
   248  			t.Fatalf("expected payload was not received: %s", err)
   249  		}
   250  		sn.UpdateForward(seqnum.Size(payloadSize))
   251  	}
   252  	closeSACKConnection(t, dut, conn, acceptFd, listenFd)
   253  }
   254  
   255  // TestRACKWithReorder tests that RACK will retransmit segments when there is
   256  // reordering in the connection and reorder timer expires.
   257  func TestRACKWithReorder(t *testing.T) {
   258  	dut, conn, acceptFd, listenFd := createSACKConnection(t)
   259  	seqNum1 := *conn.RemoteSeqNum(t)
   260  
   261  	// Send ACK for data packets to establish RTT.
   262  	sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
   263  	seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
   264  
   265  	// We are not sending ACK for these packets.
   266  	const numPkts = 4
   267  	sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
   268  
   269  	time.Sleep(simulatedRTT)
   270  	// SACK in reverse order for the connection to detect reorder.
   271  	var start seqnum.Value
   272  	var end seqnum.Value
   273  	for i := 0; i < numPkts-1; i++ {
   274  		sackBlock := make([]byte, 40)
   275  		sbOff := 0
   276  		start = seqNum1.Add(seqnum.Size((numPkts - i - 1) * payloadSize))
   277  		end = start.Add(seqnum.Size((i + 1) * payloadSize))
   278  		sackBlock = make([]byte, 40)
   279  		sbOff = 0
   280  		sbOff += header.EncodeNOP(sackBlock[sbOff:])
   281  		sbOff += header.EncodeNOP(sackBlock[sbOff:])
   282  		sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
   283  			start, end,
   284  		}}, sackBlock[sbOff:])
   285  		conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
   286  	}
   287  
   288  	// Send a DSACK block indicating both original and retransmitted
   289  	// packets are received, RACK will increase the reordering window on
   290  	// every DSACK.
   291  	dsackBlock := make([]byte, 40)
   292  	dbOff := 0
   293  	start = seqNum1
   294  	end = start.Add(seqnum.Size(2 * payloadSize))
   295  	dbOff += header.EncodeNOP(dsackBlock[dbOff:])
   296  	dbOff += header.EncodeNOP(dsackBlock[dbOff:])
   297  	dbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
   298  		start, end,
   299  	}}, dsackBlock[dbOff:])
   300  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1 + numPkts*payloadSize)), Options: dsackBlock[:dbOff]})
   301  
   302  	seqNum1.UpdateForward(seqnum.Size(numPkts * payloadSize))
   303  	sendTime := time.Now()
   304  	sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
   305  
   306  	time.Sleep(simulatedRTT)
   307  	// Send SACK for [2-5] packets.
   308  	sackBlock := make([]byte, 40)
   309  	sbOff := 0
   310  	start = seqNum1.Add(seqnum.Size(payloadSize))
   311  	end = start.Add(seqnum.Size(3 * payloadSize))
   312  	sbOff += header.EncodeNOP(sackBlock[sbOff:])
   313  	sbOff += header.EncodeNOP(sackBlock[sbOff:])
   314  	sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
   315  		start, end,
   316  	}}, sackBlock[sbOff:])
   317  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
   318  
   319  	// Expect the retransmission of #1 packet after RTT+ReorderWindow.
   320  	if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, time.Second); err != nil {
   321  		t.Fatalf("expected payload was not received: %s", err)
   322  	}
   323  	rtt, _ := getRTTAndRTO(t, dut, acceptFd)
   324  	diff := time.Now().Sub(sendTime)
   325  	if diff < rtt {
   326  		t.Fatalf("expected payload was received too sonn, within RTT")
   327  	}
   328  
   329  	closeSACKConnection(t, dut, conn, acceptFd, listenFd)
   330  }
   331  
   332  // TestRACKWithLostRetransmission tests that RACK will not enter RTO when a
   333  // retransmitted segment is lost and enters fast recovery.
   334  func TestRACKWithLostRetransmission(t *testing.T) {
   335  	dut, conn, acceptFd, listenFd := createSACKConnection(t)
   336  	seqNum1 := *conn.RemoteSeqNum(t)
   337  
   338  	// Send ACK for data packets to establish RTT.
   339  	sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
   340  	seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
   341  
   342  	// We are not sending ACK for these packets.
   343  	const numPkts = 5
   344  	sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
   345  
   346  	// SACK for [2-5] packets.
   347  	sackBlock := make([]byte, 40)
   348  	start := seqNum1.Add(seqnum.Size(payloadSize))
   349  	end := start.Add(seqnum.Size(4 * payloadSize))
   350  	sbOff := 0
   351  	sbOff += header.EncodeNOP(sackBlock[sbOff:])
   352  	sbOff += header.EncodeNOP(sackBlock[sbOff:])
   353  	sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
   354  		start, end,
   355  	}}, sackBlock[sbOff:])
   356  	time.Sleep(simulatedRTT)
   357  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
   358  
   359  	// RACK marks #1 packet as lost and retransmits it after
   360  	// RTT + reorderWindow. The reorderWindow is bounded between a small
   361  	// fraction of RTT and 1 RTT.
   362  	rtt, _ := getRTTAndRTO(t, dut, acceptFd)
   363  	timeout := 2 * rtt
   364  	if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, timeout); err != nil {
   365  		t.Fatalf("expected payload was not received: %s", err)
   366  	}
   367  
   368  	// Send #6 packet.
   369  	payload := make([]byte, payloadSize)
   370  	dut.Send(t, acceptFd, payload, 0)
   371  	gotOne, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1 + 5*payloadSize))}, time.Second)
   372  	if err != nil {
   373  		t.Fatalf("Expect #6: %s", err)
   374  	}
   375  	if gotOne == nil {
   376  		t.Fatalf("#6: expected a packet within a second but got none")
   377  	}
   378  
   379  	// SACK for [2-6] packets.
   380  	sackBlock1 := make([]byte, 40)
   381  	start = seqNum1.Add(seqnum.Size(payloadSize))
   382  	end = start.Add(seqnum.Size(5 * payloadSize))
   383  	sbOff1 := 0
   384  	sbOff1 += header.EncodeNOP(sackBlock1[sbOff1:])
   385  	sbOff1 += header.EncodeNOP(sackBlock1[sbOff1:])
   386  	sbOff1 += header.EncodeSACKBlocks([]header.SACKBlock{{
   387  		start, end,
   388  	}}, sackBlock1[sbOff1:])
   389  	time.Sleep(simulatedRTT)
   390  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock1[:sbOff1]})
   391  
   392  	// Expect re-retransmission of #1 packet without entering an RTO.
   393  	if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, timeout); err != nil {
   394  		t.Fatalf("expected payload was not received: %s", err)
   395  	}
   396  
   397  	// Check the congestion control state.
   398  	info := dut.GetSockOptTCPInfo(t, acceptFd)
   399  	if info.CaState != linux.TCP_CA_Recovery {
   400  		t.Fatalf("expected connection to be in fast recovery, want: %v got: %v", linux.TCP_CA_Recovery, info.CaState)
   401  	}
   402  
   403  	closeSACKConnection(t, dut, conn, acceptFd, listenFd)
   404  }