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 }