github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/transport/tcp/tcp_sack_test.go (about)

     1  // Copyright 2018 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_test
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"log"
    21  	"reflect"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/SagerNet/gvisor/pkg/tcpip"
    26  	"github.com/SagerNet/gvisor/pkg/tcpip/header"
    27  	"github.com/SagerNet/gvisor/pkg/tcpip/seqnum"
    28  	"github.com/SagerNet/gvisor/pkg/tcpip/stack"
    29  	"github.com/SagerNet/gvisor/pkg/tcpip/transport/tcp"
    30  	"github.com/SagerNet/gvisor/pkg/tcpip/transport/tcp/testing/context"
    31  	"github.com/SagerNet/gvisor/pkg/test/testutil"
    32  )
    33  
    34  // createConnectedWithSACKPermittedOption creates and connects c.ep with the
    35  // SACKPermitted option enabled if the stack in the context has the SACK support
    36  // enabled.
    37  func createConnectedWithSACKPermittedOption(c *context.Context) *context.RawEndpoint {
    38  	return c.CreateConnectedWithOptions(header.TCPSynOptions{SACKPermitted: c.SACKEnabled()})
    39  }
    40  
    41  // createConnectedWithSACKAndTS creates and connects c.ep with the SACK & TS
    42  // option enabled if the stack in the context has SACK and TS enabled.
    43  func createConnectedWithSACKAndTS(c *context.Context) *context.RawEndpoint {
    44  	return c.CreateConnectedWithOptions(header.TCPSynOptions{SACKPermitted: c.SACKEnabled(), TS: true})
    45  }
    46  
    47  func setStackSACKPermitted(t *testing.T, c *context.Context, enable bool) {
    48  	t.Helper()
    49  	opt := tcpip.TCPSACKEnabled(enable)
    50  	if err := c.Stack().SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
    51  		t.Fatalf("c.s.SetTransportProtocolOption(%d, &%T(%t)): %s", tcp.ProtocolNumber, opt, opt, err)
    52  	}
    53  }
    54  
    55  // TestSackPermittedConnect establishes a connection with the SACK option
    56  // enabled.
    57  func TestSackPermittedConnect(t *testing.T) {
    58  	for _, sackEnabled := range []bool{false, true} {
    59  		t.Run(fmt.Sprintf("stack.sackEnabled: %v", sackEnabled), func(t *testing.T) {
    60  			c := context.New(t, defaultMTU)
    61  			defer c.Cleanup()
    62  
    63  			setStackSACKPermitted(t, c, sackEnabled)
    64  			rep := createConnectedWithSACKPermittedOption(c)
    65  			data := []byte{1, 2, 3}
    66  
    67  			rep.SendPacket(data, nil)
    68  			savedSeqNum := rep.NextSeqNum
    69  			rep.VerifyACKNoSACK()
    70  
    71  			// Make an out of order packet and send it.
    72  			rep.NextSeqNum += 3
    73  			sackBlocks := []header.SACKBlock{
    74  				{rep.NextSeqNum, rep.NextSeqNum.Add(seqnum.Size(len(data)))},
    75  			}
    76  			rep.SendPacket(data, nil)
    77  
    78  			// Restore the saved sequence number so that the
    79  			// VerifyXXX calls use the right sequence number for
    80  			// checking ACK numbers.
    81  			rep.NextSeqNum = savedSeqNum
    82  			if sackEnabled {
    83  				rep.VerifyACKHasSACK(sackBlocks)
    84  			} else {
    85  				rep.VerifyACKNoSACK()
    86  			}
    87  
    88  			// Send the missing segment.
    89  			rep.SendPacket(data, nil)
    90  			// The ACK should contain the cumulative ACK for all 9
    91  			// bytes sent and no SACK blocks.
    92  			rep.NextSeqNum += 3
    93  			// Check that no SACK block is returned in the ACK.
    94  			rep.VerifyACKNoSACK()
    95  		})
    96  	}
    97  }
    98  
    99  // TestSackDisabledConnect establishes a connection with the SACK option
   100  // disabled and verifies that no SACKs are sent for out of order segments.
   101  func TestSackDisabledConnect(t *testing.T) {
   102  	for _, sackEnabled := range []bool{false, true} {
   103  		t.Run(fmt.Sprintf("sackEnabled: %v", sackEnabled), func(t *testing.T) {
   104  			c := context.New(t, defaultMTU)
   105  			defer c.Cleanup()
   106  
   107  			setStackSACKPermitted(t, c, sackEnabled)
   108  
   109  			rep := c.CreateConnectedWithOptions(header.TCPSynOptions{})
   110  
   111  			data := []byte{1, 2, 3}
   112  
   113  			rep.SendPacket(data, nil)
   114  			savedSeqNum := rep.NextSeqNum
   115  			rep.VerifyACKNoSACK()
   116  
   117  			// Make an out of order packet and send it.
   118  			rep.NextSeqNum += 3
   119  			rep.SendPacket(data, nil)
   120  
   121  			// The ACK should contain the older sequence number and
   122  			// no SACK blocks.
   123  			rep.NextSeqNum = savedSeqNum
   124  			rep.VerifyACKNoSACK()
   125  
   126  			// Send the missing segment.
   127  			rep.SendPacket(data, nil)
   128  			// The ACK should contain the cumulative ACK for all 9
   129  			// bytes sent and no SACK blocks.
   130  			rep.NextSeqNum += 3
   131  			// Check that no SACK block is returned in the ACK.
   132  			rep.VerifyACKNoSACK()
   133  		})
   134  	}
   135  }
   136  
   137  // TestSackPermittedAccept accepts and establishes a connection with the
   138  // SACKPermitted option enabled if the connection request specifies the
   139  // SACKPermitted option. In case of SYN cookies SACK should be disabled as we
   140  // don't encode the SACK information in the cookie.
   141  func TestSackPermittedAccept(t *testing.T) {
   142  	type testCase struct {
   143  		cookieEnabled bool
   144  		sackPermitted bool
   145  		wndScale      int
   146  		wndSize       uint16
   147  	}
   148  
   149  	testCases := []testCase{
   150  		// When cookie is used window scaling is disabled.
   151  		{true, false, -1, 0xffff}, // When cookie is used window scaling is disabled.
   152  		{false, true, 5, 0x8000},  // 0x8000 * 2^5 = 1<<20 = 1MB window (the default).
   153  	}
   154  
   155  	for _, tc := range testCases {
   156  		t.Run(fmt.Sprintf("test: %#v", tc), func(t *testing.T) {
   157  			for _, sackEnabled := range []bool{false, true} {
   158  				t.Run(fmt.Sprintf("test stack.sackEnabled: %v", sackEnabled), func(t *testing.T) {
   159  					c := context.New(t, defaultMTU)
   160  					defer c.Cleanup()
   161  
   162  					if tc.cookieEnabled {
   163  						opt := tcpip.TCPAlwaysUseSynCookies(true)
   164  						if err := c.Stack().SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
   165  							t.Fatalf("SetTransportProtocolOption(%d, &%T(%t)): %s", tcp.ProtocolNumber, opt, opt, err)
   166  						}
   167  					}
   168  					setStackSACKPermitted(t, c, sackEnabled)
   169  
   170  					rep := c.AcceptWithOptions(tc.wndScale, header.TCPSynOptions{MSS: defaultIPv4MSS, SACKPermitted: tc.sackPermitted})
   171  					//  Now verify no SACK blocks are
   172  					//  received when sack is disabled.
   173  					data := []byte{1, 2, 3}
   174  					rep.SendPacket(data, nil)
   175  					rep.VerifyACKNoSACK()
   176  
   177  					savedSeqNum := rep.NextSeqNum
   178  
   179  					// Make an out of order packet and send
   180  					// it.
   181  					rep.NextSeqNum += 3
   182  					sackBlocks := []header.SACKBlock{
   183  						{rep.NextSeqNum, rep.NextSeqNum.Add(seqnum.Size(len(data)))},
   184  					}
   185  					rep.SendPacket(data, nil)
   186  
   187  					// The ACK should contain the older
   188  					// sequence number.
   189  					rep.NextSeqNum = savedSeqNum
   190  					if sackEnabled && tc.sackPermitted {
   191  						rep.VerifyACKHasSACK(sackBlocks)
   192  					} else {
   193  						rep.VerifyACKNoSACK()
   194  					}
   195  
   196  					// Send the missing segment.
   197  					rep.SendPacket(data, nil)
   198  					// The ACK should contain the cumulative
   199  					// ACK for all 9 bytes sent and no SACK
   200  					// blocks.
   201  					rep.NextSeqNum += 3
   202  					// Check that no SACK block is returned
   203  					// in the ACK.
   204  					rep.VerifyACKNoSACK()
   205  				})
   206  			}
   207  		})
   208  	}
   209  }
   210  
   211  // TestSackDisabledAccept accepts and establishes a connection with
   212  // the SACKPermitted option disabled and verifies that no SACKs are
   213  // sent for out of order packets.
   214  func TestSackDisabledAccept(t *testing.T) {
   215  	type testCase struct {
   216  		cookieEnabled bool
   217  		wndScale      int
   218  		wndSize       uint16
   219  	}
   220  
   221  	testCases := []testCase{
   222  		// When cookie is used window scaling is disabled.
   223  		{true, -1, 0xffff}, // When cookie is used window scaling is disabled.
   224  		{false, 5, 0x8000}, // 0x8000 * 2^5 = 1<<20 = 1MB window (the default).
   225  	}
   226  
   227  	for _, tc := range testCases {
   228  		t.Run(fmt.Sprintf("test: %#v", tc), func(t *testing.T) {
   229  			for _, sackEnabled := range []bool{false, true} {
   230  				t.Run(fmt.Sprintf("test: sackEnabled: %v", sackEnabled), func(t *testing.T) {
   231  					c := context.New(t, defaultMTU)
   232  					defer c.Cleanup()
   233  
   234  					if tc.cookieEnabled {
   235  						opt := tcpip.TCPAlwaysUseSynCookies(true)
   236  						if err := c.Stack().SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
   237  							t.Fatalf("SetTransportProtocolOption(%d, &%T(%t)): %s", tcp.ProtocolNumber, opt, opt, err)
   238  						}
   239  					}
   240  
   241  					setStackSACKPermitted(t, c, sackEnabled)
   242  
   243  					rep := c.AcceptWithOptions(tc.wndScale, header.TCPSynOptions{MSS: defaultIPv4MSS})
   244  
   245  					//  Now verify no SACK blocks are
   246  					//  received when sack is disabled.
   247  					data := []byte{1, 2, 3}
   248  					rep.SendPacket(data, nil)
   249  					rep.VerifyACKNoSACK()
   250  					savedSeqNum := rep.NextSeqNum
   251  
   252  					// Make an out of order packet and send
   253  					// it.
   254  					rep.NextSeqNum += 3
   255  					rep.SendPacket(data, nil)
   256  
   257  					// The ACK should contain the older
   258  					// sequence number and no SACK blocks.
   259  					rep.NextSeqNum = savedSeqNum
   260  					rep.VerifyACKNoSACK()
   261  
   262  					// Send the missing segment.
   263  					rep.SendPacket(data, nil)
   264  					// The ACK should contain the cumulative
   265  					// ACK for all 9 bytes sent and no SACK
   266  					// blocks.
   267  					rep.NextSeqNum += 3
   268  					// Check that no SACK block is returned
   269  					// in the ACK.
   270  					rep.VerifyACKNoSACK()
   271  				})
   272  			}
   273  		})
   274  	}
   275  }
   276  
   277  func TestUpdateSACKBlocks(t *testing.T) {
   278  	testCases := []struct {
   279  		segStart   seqnum.Value
   280  		segEnd     seqnum.Value
   281  		rcvNxt     seqnum.Value
   282  		sackBlocks []header.SACKBlock
   283  		updated    []header.SACKBlock
   284  	}{
   285  		// Trivial cases where current SACK block list is empty and we
   286  		// have an out of order delivery.
   287  		{10, 11, 2, []header.SACKBlock{}, []header.SACKBlock{{10, 11}}},
   288  		{10, 12, 2, []header.SACKBlock{}, []header.SACKBlock{{10, 12}}},
   289  		{10, 20, 2, []header.SACKBlock{}, []header.SACKBlock{{10, 20}}},
   290  
   291  		// Cases where current SACK block list is not empty and we have
   292  		// an out of order delivery. Tests that the updated SACK block
   293  		// list has the first block as the one that contains the new
   294  		// SACK block representing the segment that was just delivered.
   295  		{10, 11, 9, []header.SACKBlock{{12, 20}}, []header.SACKBlock{{10, 11}, {12, 20}}},
   296  		{24, 30, 9, []header.SACKBlock{{12, 20}}, []header.SACKBlock{{24, 30}, {12, 20}}},
   297  		{24, 30, 9, []header.SACKBlock{{12, 20}, {32, 40}}, []header.SACKBlock{{24, 30}, {12, 20}, {32, 40}}},
   298  
   299  		// Ensure that we only retain header.MaxSACKBlocks and drop the
   300  		// oldest one if adding a new block exceeds
   301  		// header.MaxSACKBlocks.
   302  		{24, 30, 9,
   303  			[]header.SACKBlock{{12, 20}, {32, 40}, {42, 50}, {52, 60}, {62, 70}, {72, 80}},
   304  			[]header.SACKBlock{{24, 30}, {12, 20}, {32, 40}, {42, 50}, {52, 60}, {62, 70}}},
   305  
   306  		// Cases where segment extends an existing SACK block.
   307  		{10, 12, 9, []header.SACKBlock{{12, 20}}, []header.SACKBlock{{10, 20}}},
   308  		{10, 22, 9, []header.SACKBlock{{12, 20}}, []header.SACKBlock{{10, 22}}},
   309  		{10, 22, 9, []header.SACKBlock{{12, 20}}, []header.SACKBlock{{10, 22}}},
   310  		{15, 22, 9, []header.SACKBlock{{12, 20}}, []header.SACKBlock{{12, 22}}},
   311  		{15, 25, 9, []header.SACKBlock{{12, 20}}, []header.SACKBlock{{12, 25}}},
   312  		{11, 25, 9, []header.SACKBlock{{12, 20}}, []header.SACKBlock{{11, 25}}},
   313  		{10, 12, 9, []header.SACKBlock{{12, 20}, {32, 40}}, []header.SACKBlock{{10, 20}, {32, 40}}},
   314  		{10, 22, 9, []header.SACKBlock{{12, 20}, {32, 40}}, []header.SACKBlock{{10, 22}, {32, 40}}},
   315  		{10, 22, 9, []header.SACKBlock{{12, 20}, {32, 40}}, []header.SACKBlock{{10, 22}, {32, 40}}},
   316  		{15, 22, 9, []header.SACKBlock{{12, 20}, {32, 40}}, []header.SACKBlock{{12, 22}, {32, 40}}},
   317  		{15, 25, 9, []header.SACKBlock{{12, 20}, {32, 40}}, []header.SACKBlock{{12, 25}, {32, 40}}},
   318  		{11, 25, 9, []header.SACKBlock{{12, 20}, {32, 40}}, []header.SACKBlock{{11, 25}, {32, 40}}},
   319  
   320  		// Cases where segment contains rcvNxt.
   321  		{10, 20, 15, []header.SACKBlock{{20, 30}, {40, 50}}, []header.SACKBlock{{40, 50}}},
   322  	}
   323  
   324  	for _, tc := range testCases {
   325  		var sack tcp.SACKInfo
   326  		copy(sack.Blocks[:], tc.sackBlocks)
   327  		sack.NumBlocks = len(tc.sackBlocks)
   328  		tcp.UpdateSACKBlocks(&sack, tc.segStart, tc.segEnd, tc.rcvNxt)
   329  		if got, want := sack.Blocks[:sack.NumBlocks], tc.updated; !reflect.DeepEqual(got, want) {
   330  			t.Errorf("UpdateSACKBlocks(%v, %v, %v, %v), got: %v, want: %v", tc.sackBlocks, tc.segStart, tc.segEnd, tc.rcvNxt, got, want)
   331  		}
   332  
   333  	}
   334  }
   335  
   336  func TestTrimSackBlockList(t *testing.T) {
   337  	testCases := []struct {
   338  		rcvNxt     seqnum.Value
   339  		sackBlocks []header.SACKBlock
   340  		trimmed    []header.SACKBlock
   341  	}{
   342  		// Simple cases where we trim whole entries.
   343  		{2, []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}}, []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}}},
   344  		{21, []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}}, []header.SACKBlock{{22, 30}, {32, 40}}},
   345  		{31, []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}}, []header.SACKBlock{{32, 40}}},
   346  		{40, []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}}, []header.SACKBlock{}},
   347  		// Cases where we need to update a block.
   348  		{12, []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}}, []header.SACKBlock{{12, 20}, {22, 30}, {32, 40}}},
   349  		{23, []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}}, []header.SACKBlock{{23, 30}, {32, 40}}},
   350  		{33, []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}}, []header.SACKBlock{{33, 40}}},
   351  		{41, []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}}, []header.SACKBlock{}},
   352  	}
   353  	for _, tc := range testCases {
   354  		var sack tcp.SACKInfo
   355  		copy(sack.Blocks[:], tc.sackBlocks)
   356  		sack.NumBlocks = len(tc.sackBlocks)
   357  		tcp.TrimSACKBlockList(&sack, tc.rcvNxt)
   358  		if got, want := sack.Blocks[:sack.NumBlocks], tc.trimmed; !reflect.DeepEqual(got, want) {
   359  			t.Errorf("TrimSackBlockList(%v, %v), got: %v, want: %v", tc.sackBlocks, tc.rcvNxt, got, want)
   360  		}
   361  	}
   362  }
   363  
   364  func TestSACKRecovery(t *testing.T) {
   365  	const maxPayload = 10
   366  	// See: tcp.makeOptions for why tsOptionSize is set to 12 here.
   367  	const tsOptionSize = 12
   368  	// Enabling SACK means the payload size is reduced to account
   369  	// for the extra space required for the TCP options.
   370  	//
   371  	// We increase the MTU by 40 bytes to account for SACK and Timestamp
   372  	// options.
   373  	const maxTCPOptionSize = 40
   374  
   375  	c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxTCPOptionSize+maxPayload))
   376  	defer c.Cleanup()
   377  
   378  	c.Stack().AddTCPProbe(func(s stack.TCPEndpointState) {
   379  		// We use log.Printf instead of t.Logf here because this probe
   380  		// can fire even when the test function has finished. This is
   381  		// because closing the endpoint in cleanup() does not mean the
   382  		// actual worker loop terminates immediately as it still has to
   383  		// do a full TCP shutdown. But this test can finish running
   384  		// before the shutdown is done. Using t.Logf in such a case
   385  		// causes the test to panic due to logging after test finished.
   386  		log.Printf("state: %+v\n", s)
   387  	})
   388  	setStackSACKPermitted(t, c, true)
   389  	createConnectedWithSACKAndTS(c)
   390  
   391  	const iterations = 3
   392  	data := make([]byte, 2*maxPayload*(tcp.InitialCwnd<<(iterations+1)))
   393  	for i := range data {
   394  		data[i] = byte(i)
   395  	}
   396  
   397  	// Write all the data in one shot. Packets will only be written at the
   398  	// MTU size though.
   399  	var r bytes.Reader
   400  	r.Reset(data)
   401  	if _, err := c.EP.Write(&r, tcpip.WriteOptions{}); err != nil {
   402  		t.Fatalf("Write failed: %s", err)
   403  	}
   404  
   405  	// Do slow start for a few iterations.
   406  	seq := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
   407  	expected := tcp.InitialCwnd
   408  	bytesRead := 0
   409  	for i := 0; i < iterations; i++ {
   410  		expected = tcp.InitialCwnd << uint(i)
   411  		if i > 0 {
   412  			// Acknowledge all the data received so far if not on
   413  			// first iteration.
   414  			c.SendAck(seq, bytesRead)
   415  		}
   416  
   417  		// Read all packets expected on this iteration. Don't
   418  		// acknowledge any of them just yet, so that we can measure the
   419  		// congestion window.
   420  		for j := 0; j < expected; j++ {
   421  			c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
   422  			bytesRead += maxPayload
   423  		}
   424  
   425  		// Check we don't receive any more packets on this iteration.
   426  		// The timeout can't be too high or we'll trigger a timeout.
   427  		c.CheckNoPacketTimeout("More packets received than expected for this cwnd.", 50*time.Millisecond)
   428  	}
   429  
   430  	// Send 3 duplicate acks. This should force an immediate retransmit of
   431  	// the pending packet and put the sender into fast recovery.
   432  	rtxOffset := bytesRead - maxPayload*expected
   433  	start := c.IRS.Add(seqnum.Size(rtxOffset) + 30 + 1)
   434  	end := start.Add(10)
   435  	for i := 0; i < 3; i++ {
   436  		c.SendAckWithSACK(seq, rtxOffset, []header.SACKBlock{{start, end}})
   437  		end = end.Add(10)
   438  	}
   439  
   440  	// Receive the retransmitted packet.
   441  	c.ReceiveAndCheckPacketWithOptions(data, rtxOffset, maxPayload, tsOptionSize)
   442  
   443  	metricPollFn := func() error {
   444  		tcpStats := c.Stack().Stats().TCP
   445  		stats := []struct {
   446  			stat *tcpip.StatCounter
   447  			name string
   448  			want uint64
   449  		}{
   450  			{tcpStats.FastRetransmit, "stats.TCP.FastRetransmit", 1},
   451  			{tcpStats.Retransmits, "stats.TCP.Retransmits", 1},
   452  			{tcpStats.SACKRecovery, "stats.TCP.SACKRecovery", 1},
   453  			{tcpStats.FastRecovery, "stats.TCP.FastRecovery", 0},
   454  		}
   455  		for _, s := range stats {
   456  			if got, want := s.stat.Value(), s.want; got != want {
   457  				return fmt.Errorf("got %s.Value() = %d, want = %d", s.name, got, want)
   458  			}
   459  		}
   460  		return nil
   461  	}
   462  
   463  	if err := testutil.Poll(metricPollFn, 1*time.Second); err != nil {
   464  		t.Error(err)
   465  	}
   466  
   467  	// Now send 7 mode duplicate ACKs. In SACK TCP dupAcks do not cause
   468  	// window inflation and sending of packets is completely handled by the
   469  	// SACK Recovery algorithm. We should see no packets being released, as
   470  	// the cwnd at this point after entering recovery should be half of the
   471  	// outstanding number of packets in flight.
   472  	for i := 0; i < 7; i++ {
   473  		c.SendAckWithSACK(seq, rtxOffset, []header.SACKBlock{{start, end}})
   474  		end = end.Add(10)
   475  	}
   476  
   477  	recover := bytesRead
   478  
   479  	// Ensure no new packets arrive.
   480  	c.CheckNoPacketTimeout("More packets received than expected during recovery after dupacks for this cwnd.",
   481  		50*time.Millisecond)
   482  
   483  	// Acknowledge half of the pending data. This along with the 10 sacked
   484  	// segments above should reduce the outstanding below the current
   485  	// congestion window allowing the sender to transmit data.
   486  	rtxOffset = bytesRead - expected*maxPayload/2
   487  
   488  	// Now send a partial ACK w/ a SACK block that indicates that the next 3
   489  	// segments are lost and we have received 6 segments after the lost
   490  	// segments. This should cause the sender to immediately transmit all 3
   491  	// segments in response to this ACK unlike in FastRecovery where only 1
   492  	// segment is retransmitted per ACK.
   493  	start = c.IRS.Add(seqnum.Size(rtxOffset) + 30 + 1)
   494  	end = start.Add(60)
   495  	c.SendAckWithSACK(seq, rtxOffset, []header.SACKBlock{{start, end}})
   496  
   497  	// At this point, we acked expected/2 packets and we SACKED 6 packets and
   498  	// 3 segments were considered lost due to the SACK block we sent.
   499  	//
   500  	// So total packets outstanding can be calculated as follows after 7
   501  	// iterations of slow start -> 10/20/40/80/160/320/640. So expected
   502  	// should be 640 at start, then we went to recover at which point the
   503  	// cwnd should be set to 320 + 3 (for the 3 dupAcks which have left the
   504  	// network).
   505  	// Outstanding at this point after acking half the window
   506  	// (320 packets) will be:
   507  	//    outstanding = 640-320-6(due to SACK block)-3 = 311
   508  	//
   509  	// The last 3 is due to the fact that the first 3 packets after
   510  	// rtxOffset will be considered lost due to the SACK blocks sent.
   511  	// Receive the retransmit due to partial ack.
   512  
   513  	c.ReceiveAndCheckPacketWithOptions(data, rtxOffset, maxPayload, tsOptionSize)
   514  	// Receive the 2 extra packets that should have been retransmitted as
   515  	// those should be considered lost and immediately retransmitted based
   516  	// on the SACK information in the previous ACK sent above.
   517  	for i := 0; i < 2; i++ {
   518  		c.ReceiveAndCheckPacketWithOptions(data, rtxOffset+maxPayload*(i+1), maxPayload, tsOptionSize)
   519  	}
   520  
   521  	// Now we should get 9 more new unsent packets as the cwnd is 323 and
   522  	// outstanding is 311.
   523  	for i := 0; i < 9; i++ {
   524  		c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
   525  		bytesRead += maxPayload
   526  	}
   527  
   528  	metricPollFn = func() error {
   529  		// In SACK recovery only the first segment is fast retransmitted when
   530  		// entering recovery.
   531  		if got, want := c.Stack().Stats().TCP.FastRetransmit.Value(), uint64(1); got != want {
   532  			return fmt.Errorf("got stats.TCP.FastRetransmit.Value = %d, want = %d", got, want)
   533  		}
   534  
   535  		if got, want := c.EP.Stats().(*tcp.Stats).SendErrors.FastRetransmit.Value(), uint64(1); got != want {
   536  			return fmt.Errorf("got EP stats SendErrors.FastRetransmit = %d, want = %d", got, want)
   537  		}
   538  
   539  		if got, want := c.Stack().Stats().TCP.Retransmits.Value(), uint64(4); got != want {
   540  			return fmt.Errorf("got stats.TCP.Retransmits.Value = %d, want = %d", got, want)
   541  		}
   542  
   543  		if got, want := c.EP.Stats().(*tcp.Stats).SendErrors.Retransmits.Value(), uint64(4); got != want {
   544  			return fmt.Errorf("got EP stats Stats.SendErrors.Retransmits = %d, want = %d", got, want)
   545  		}
   546  		return nil
   547  	}
   548  	if err := testutil.Poll(metricPollFn, 1*time.Second); err != nil {
   549  		t.Error(err)
   550  	}
   551  
   552  	c.CheckNoPacketTimeout("More packets received than expected during recovery after partial ack for this cwnd.", 50*time.Millisecond)
   553  
   554  	// Acknowledge all pending data to recover point.
   555  	c.SendAck(seq, recover)
   556  
   557  	// At this point, the cwnd should reset to expected/2 and there are 9
   558  	// packets outstanding.
   559  	//
   560  	// Now in the first iteration since there are 9 packets outstanding.
   561  	// We would expect to get expected/2  - 9 packets. But subsequent
   562  	// iterations will send us expected/2  + 1 (per iteration).
   563  	expected = expected/2 - 9
   564  	for i := 0; i < iterations; i++ {
   565  		// Read all packets expected on this iteration. Don't
   566  		// acknowledge any of them just yet, so that we can measure the
   567  		// congestion window.
   568  		for j := 0; j < expected; j++ {
   569  			c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
   570  			bytesRead += maxPayload
   571  		}
   572  		// Check we don't receive any more packets on this iteration.
   573  		// The timeout can't be too high or we'll trigger a timeout.
   574  		c.CheckNoPacketTimeout(fmt.Sprintf("More packets received(after deflation) than expected %d for this cwnd and iteration: %d.", expected, i), 50*time.Millisecond)
   575  
   576  		// Acknowledge all the data received so far.
   577  		c.SendAck(seq, bytesRead)
   578  
   579  		// In cogestion avoidance, the packets trains increase by 1 in
   580  		// each iteration.
   581  		if i == 0 {
   582  			// After the first iteration we expect to get the full
   583  			// congestion window worth of packets in every
   584  			// iteration.
   585  			expected += 9
   586  		}
   587  		expected++
   588  	}
   589  }
   590  
   591  // TestRecoveryEntry tests the following two properties of entering recovery:
   592  // - Fast SACK recovery is entered when SND.UNA is considered lost by the SACK
   593  //   scoreboard but dupack count is still below threshold.
   594  // - Only enter recovery when at least one more byte of data beyond the highest
   595  //   byte that was outstanding when fast retransmit was last entered is acked.
   596  func TestRecoveryEntry(t *testing.T) {
   597  	c := context.New(t, uint32(mtu))
   598  	defer c.Cleanup()
   599  
   600  	numPackets := 5
   601  	data := sendAndReceiveWithSACK(t, c, numPackets, false /* enableRACK */)
   602  
   603  	// Ack #1 packet.
   604  	seq := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
   605  	c.SendAck(seq, maxPayload)
   606  
   607  	// Now SACK #3, #4 and #5 packets. This will simulate a situation where
   608  	// SND.UNA should be considered lost and the sender should enter fast recovery
   609  	// (even though dupack count is still below threshold).
   610  	p3Start := c.IRS.Add(1 + seqnum.Size(2*maxPayload))
   611  	p3End := p3Start.Add(maxPayload)
   612  	p4Start := p3End
   613  	p4End := p4Start.Add(maxPayload)
   614  	p5Start := p4End
   615  	p5End := p5Start.Add(maxPayload)
   616  	c.SendAckWithSACK(seq, maxPayload, []header.SACKBlock{{p3Start, p3End}, {p4Start, p4End}, {p5Start, p5End}})
   617  
   618  	// Expect #2 to be retransmitted.
   619  	c.ReceiveAndCheckPacketWithOptions(data, maxPayload, maxPayload, tsOptionSize)
   620  
   621  	metricPollFn := func() error {
   622  		tcpStats := c.Stack().Stats().TCP
   623  		stats := []struct {
   624  			stat *tcpip.StatCounter
   625  			name string
   626  			want uint64
   627  		}{
   628  			// SACK recovery must have happened.
   629  			{tcpStats.FastRetransmit, "stats.TCP.FastRetransmit", 1},
   630  			{tcpStats.SACKRecovery, "stats.TCP.SACKRecovery", 1},
   631  			// #2 was retransmitted.
   632  			{tcpStats.Retransmits, "stats.TCP.Retransmits", 1},
   633  			// No RTOs should have fired yet.
   634  			{tcpStats.Timeouts, "stats.TCP.Timeouts", 0},
   635  		}
   636  		for _, s := range stats {
   637  			if got, want := s.stat.Value(), s.want; got != want {
   638  				return fmt.Errorf("got %s.Value() = %d, want = %d", s.name, got, want)
   639  			}
   640  		}
   641  		return nil
   642  	}
   643  	if err := testutil.Poll(metricPollFn, 1*time.Second); err != nil {
   644  		t.Error(err)
   645  	}
   646  
   647  	// Send 4 more packets.
   648  	var r bytes.Reader
   649  	data = append(data, data...)
   650  	r.Reset(data[5*maxPayload : 9*maxPayload])
   651  	if _, err := c.EP.Write(&r, tcpip.WriteOptions{}); err != nil {
   652  		t.Fatalf("Write failed: %s", err)
   653  	}
   654  
   655  	var sackBlocks []header.SACKBlock
   656  	bytesRead := numPackets * maxPayload
   657  	for i := 0; i < 4; i++ {
   658  		c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
   659  		if i > 0 {
   660  			pStart := c.IRS.Add(1 + seqnum.Size(bytesRead))
   661  			sackBlocks = append(sackBlocks, header.SACKBlock{pStart, pStart.Add(maxPayload)})
   662  			c.SendAckWithSACK(seq, 5*maxPayload, sackBlocks)
   663  		}
   664  		bytesRead += maxPayload
   665  	}
   666  
   667  	// #6 should be retransmitted after RTO. The sender should NOT enter fast
   668  	// recovery because the highest byte that was outstanding when fast recovery
   669  	// was last entered is #5 packet's end. And the sender requires at least one
   670  	// more byte beyond that (#6 packet start) to be acked to enter recovery.
   671  	c.ReceiveAndCheckPacketWithOptions(data, 5*maxPayload, maxPayload, tsOptionSize)
   672  	c.SendAck(seq, 9*maxPayload)
   673  
   674  	metricPollFn = func() error {
   675  		tcpStats := c.Stack().Stats().TCP
   676  		stats := []struct {
   677  			stat *tcpip.StatCounter
   678  			name string
   679  			want uint64
   680  		}{
   681  			// Only 1 SACK recovery must have happened.
   682  			{tcpStats.FastRetransmit, "stats.TCP.FastRetransmit", 1},
   683  			{tcpStats.SACKRecovery, "stats.TCP.SACKRecovery", 1},
   684  			// #2 and #6 were retransmitted.
   685  			{tcpStats.Retransmits, "stats.TCP.Retransmits", 2},
   686  			// RTO should have fired once.
   687  			{tcpStats.Timeouts, "stats.TCP.Timeouts", 1},
   688  		}
   689  		for _, s := range stats {
   690  			if got, want := s.stat.Value(), s.want; got != want {
   691  				return fmt.Errorf("got %s.Value() = %d, want = %d", s.name, got, want)
   692  			}
   693  		}
   694  		return nil
   695  	}
   696  	if err := testutil.Poll(metricPollFn, 1*time.Second); err != nil {
   697  		t.Error(err)
   698  	}
   699  }