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 }