Add RACK reorder tests.

PiperOrigin-RevId: 355067082
This commit is contained in:
Nayana Bidari
2021-02-01 18:00:38 -08:00
committed by gVisor bot
parent 5d8054e75a
commit d3855ad6bd
+202 -2
View File
@@ -70,8 +70,11 @@ func closeSACKConnection(t *testing.T, dut testbench.DUT, conn testbench.TCPIPv4
func getRTTAndRTO(t *testing.T, dut testbench.DUT, acceptFd int32) (rtt, rto time.Duration) {
info := linux.TCPInfo{}
ret := dut.GetSockOpt(t, acceptFd, unix.SOL_TCP, unix.TCP_INFO, int32(linux.SizeOfTCPInfo))
binary.Unmarshal(ret, usermem.ByteOrder, &info)
infoBytes := dut.GetSockOpt(t, acceptFd, unix.SOL_TCP, unix.TCP_INFO, int32(linux.SizeOfTCPInfo))
if got, want := len(infoBytes), linux.SizeOfTCPInfo; got != want {
t.Fatalf("expected %T, got %d bytes want %d bytes", info, got, want)
}
binary.Unmarshal(infoBytes, usermem.ByteOrder, &info)
return time.Duration(info.RTT) * time.Microsecond, time.Duration(info.RTO) * time.Microsecond
}
@@ -219,3 +222,200 @@ func TestRACKTLPWithSACK(t *testing.T) {
}
closeSACKConnection(t, dut, conn, acceptFd, listenFd)
}
// TestRACKWithoutReorder tests that without reordering RACK will retransmit the
// lost packets after reorder timer expires.
func TestRACKWithoutReorder(t *testing.T) {
dut, conn, acceptFd, listenFd := createSACKConnection(t)
seqNum1 := *conn.RemoteSeqNum(t)
// Send ACK for data packets to establish RTT.
sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
// We are not sending ACK for these packets.
const numPkts = 4
sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
// SACK for [3,4] packets.
sackBlock := make([]byte, 40)
start := seqNum1.Add(seqnum.Size(2 * payloadSize))
end := start.Add(seqnum.Size(2 * payloadSize))
sbOff := 0
sbOff += header.EncodeNOP(sackBlock[sbOff:])
sbOff += header.EncodeNOP(sackBlock[sbOff:])
sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
start, end,
}}, sackBlock[sbOff:])
time.Sleep(simulatedRTT)
conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
// RACK marks #1 and #2 packets as lost and retransmits both after
// RTT + reorderWindow. The reorderWindow initially will be a small
// fraction of RTT.
rtt, _ := getRTTAndRTO(t, dut, acceptFd)
timeout := 2 * rtt
for i, sn := 0, seqNum1; i < 2; i++ {
if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(sn))}, timeout); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
sn.UpdateForward(seqnum.Size(payloadSize))
}
closeSACKConnection(t, dut, conn, acceptFd, listenFd)
}
// TestRACKWithReorder tests that RACK will retransmit segments when there is
// reordering in the connection and reorder timer expires.
func TestRACKWithReorder(t *testing.T) {
dut, conn, acceptFd, listenFd := createSACKConnection(t)
seqNum1 := *conn.RemoteSeqNum(t)
// Send ACK for data packets to establish RTT.
sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
// We are not sending ACK for these packets.
const numPkts = 4
sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
time.Sleep(simulatedRTT)
// SACK in reverse order for the connection to detect reorder.
var start seqnum.Value
var end seqnum.Value
for i := 0; i < numPkts-1; i++ {
sackBlock := make([]byte, 40)
sbOff := 0
start = seqNum1.Add(seqnum.Size((numPkts - i - 1) * payloadSize))
end = start.Add(seqnum.Size((i + 1) * payloadSize))
sackBlock = make([]byte, 40)
sbOff = 0
sbOff += header.EncodeNOP(sackBlock[sbOff:])
sbOff += header.EncodeNOP(sackBlock[sbOff:])
sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
start, end,
}}, sackBlock[sbOff:])
conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
}
// Send a DSACK block indicating both original and retransmitted
// packets are received, RACK will increase the reordering window on
// every DSACK.
dsackBlock := make([]byte, 40)
dbOff := 0
start = seqNum1
end = start.Add(seqnum.Size(2 * payloadSize))
dbOff += header.EncodeNOP(dsackBlock[dbOff:])
dbOff += header.EncodeNOP(dsackBlock[dbOff:])
dbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
start, end,
}}, dsackBlock[dbOff:])
conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1 + numPkts*payloadSize)), Options: dsackBlock[:dbOff]})
seqNum1.UpdateForward(seqnum.Size(numPkts * payloadSize))
sendTime := time.Now()
sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
time.Sleep(simulatedRTT)
// Send SACK for [2-5] packets.
sackBlock := make([]byte, 40)
sbOff := 0
start = seqNum1.Add(seqnum.Size(payloadSize))
end = start.Add(seqnum.Size(3 * payloadSize))
sbOff += header.EncodeNOP(sackBlock[sbOff:])
sbOff += header.EncodeNOP(sackBlock[sbOff:])
sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
start, end,
}}, sackBlock[sbOff:])
conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
// Expect the retransmission of #1 packet after RTT+ReorderWindow.
if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, time.Second); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
rtt, _ := getRTTAndRTO(t, dut, acceptFd)
diff := time.Now().Sub(sendTime)
if diff < rtt {
t.Fatalf("expected payload was received too sonn, within RTT")
}
closeSACKConnection(t, dut, conn, acceptFd, listenFd)
}
// TestRACKWithLostRetransmission tests that RACK will not enter RTO when a
// retransmitted segment is lost and enters fast recovery.
func TestRACKWithLostRetransmission(t *testing.T) {
dut, conn, acceptFd, listenFd := createSACKConnection(t)
seqNum1 := *conn.RemoteSeqNum(t)
// Send ACK for data packets to establish RTT.
sendAndReceive(t, dut, conn, numPktsForRTT, acceptFd, true /* sendACK */)
seqNum1.UpdateForward(seqnum.Size(numPktsForRTT * payloadSize))
// We are not sending ACK for these packets.
const numPkts = 5
sendAndReceive(t, dut, conn, numPkts, acceptFd, false /* sendACK */)
// SACK for [2-5] packets.
sackBlock := make([]byte, 40)
start := seqNum1.Add(seqnum.Size(payloadSize))
end := start.Add(seqnum.Size(4 * payloadSize))
sbOff := 0
sbOff += header.EncodeNOP(sackBlock[sbOff:])
sbOff += header.EncodeNOP(sackBlock[sbOff:])
sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
start, end,
}}, sackBlock[sbOff:])
time.Sleep(simulatedRTT)
conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
// RACK marks #1 packet as lost and retransmits it after
// RTT + reorderWindow. The reorderWindow is bounded between a small
// fraction of RTT and 1 RTT.
rtt, _ := getRTTAndRTO(t, dut, acceptFd)
timeout := 2 * rtt
if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, timeout); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
// Send #6 packet.
payload := make([]byte, payloadSize)
dut.Send(t, acceptFd, payload, 0)
gotOne, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1 + 5*payloadSize))}, time.Second)
if err != nil {
t.Fatalf("Expect #6: %s", err)
}
if gotOne == nil {
t.Fatalf("#6: expected a packet within a second but got none")
}
// SACK for [2-6] packets.
sackBlock1 := make([]byte, 40)
start = seqNum1.Add(seqnum.Size(payloadSize))
end = start.Add(seqnum.Size(5 * payloadSize))
sbOff1 := 0
sbOff1 += header.EncodeNOP(sackBlock1[sbOff1:])
sbOff1 += header.EncodeNOP(sackBlock1[sbOff1:])
sbOff1 += header.EncodeSACKBlocks([]header.SACKBlock{{
start, end,
}}, sackBlock1[sbOff1:])
time.Sleep(simulatedRTT)
conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: testbench.Uint32(uint32(seqNum1)), Options: sackBlock1[:sbOff1]})
// Expect re-retransmission of #1 packet without entering an RTO.
if _, err := conn.Expect(t, testbench.TCP{SeqNum: testbench.Uint32(uint32(seqNum1))}, timeout); err != nil {
t.Fatalf("expected payload was not received: %s", err)
}
// Check the congestion control state.
info := linux.TCPInfo{}
infoBytes := dut.GetSockOpt(t, acceptFd, unix.SOL_TCP, unix.TCP_INFO, int32(linux.SizeOfTCPInfo))
if got, want := len(infoBytes), linux.SizeOfTCPInfo; got != want {
t.Fatalf("expected %T, got %d bytes want %d bytes", info, got, want)
}
binary.Unmarshal(infoBytes, usermem.ByteOrder, &info)
if info.CaState != linux.TCP_CA_Recovery {
t.Fatalf("expected connection to be in fast recovery, want: %v got: %v", linux.TCP_CA_Recovery, info.CaState)
}
closeSACKConnection(t, dut, conn, acceptFd, listenFd)
}