diff --git a/gtests/net/tcp/cubic/cubic-bulk-166k-idle-restart.pkt b/gtests/net/tcp/cubic/cubic-bulk-166k-idle-restart.pkt new file mode 100644 index 00000000..b2ee30e0 --- /dev/null +++ b/gtests/net/tcp/cubic/cubic-bulk-166k-idle-restart.pkt @@ -0,0 +1,111 @@ +// Test bulk CUBIC flow at 166 kbit/sec (2 1000-byte payloads each 100ms +// means 2 * 1040 * 8 / .1 = 166400 bits/sec). +// This variant checks to see if CUBIC grows its cwnd unreasonably +// due to crediting itself for time accumulated in an idle state. + +// For this test We mostly just care about cwnd values, not exact timing. +// So to reduce flakes allow ~20ms of timing variation: +--tolerance_usecs=20000 + +`../../common/defaults.sh +tc qdisc replace dev tun0 root fq +sysctl -q net.ipv4.tcp_congestion_control=cubic +sysctl -q net.ipv4.tcp_min_tso_segs=4 +# Disable TLP to avoid flakes from spurious retransmits: +sysctl -q net.ipv4.tcp_early_retrans=0` + +// Initialize connection + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + + +0 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 + +.1 < . 1:1(0) ack 1 win 514 + + +0 accept(3, ..., ...) = 4 + + +0 setsockopt(4, SOL_SOCKET, SO_SNDBUF, [2000000], 4) = 0 + +0 %{ assert tcpi_snd_cwnd == 10, tcpi_snd_cwnd }% + +0 write(4, ..., 40000) = 40000 + + +.1 < . 1:1(0) ack 2001 win 514 + +.1 < . 1:1(0) ack 4001 win 514 + +.1 < . 1:1(0) ack 6001 win 514 + +.1 < . 1:1(0) ack 8001 win 514 + +.1 < . 1:1(0) ack 10001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 20, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 12001 win 514 + +.1 < . 1:1(0) ack 14001 win 514 + +.1 < . 1:1(0) ack 16001 win 514 + +.1 < . 1:1(0) ack 18001 win 514 + +.1 < . 1:1(0) ack 20001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 30, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 22001 win 514 + +.1 < . 1:1(0) ack 24001 win 514 + +.1 < . 1:1(0) ack 26001 win 514 + +.1 < . 1:1(0) ack 28001 win 514 + +.1 < . 1:1(0) ack 30001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 40, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 32001 win 514 + +.1 < . 1:1(0) ack 34001 win 514 + +.1 < . 1:1(0) ack 36001 win 514 + +.1 < . 1:1(0) ack 38001 win 514 +// Hystart exits slow start here at a cwnd of 48: + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 48, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 40001 win 514 + +// Go idle for a while in order to verify that this doesn't let us +// accumulate "credit" toward increasing cwnd quickly when we go +// cwnd-limited again. + +4 write(4, ..., 160000) = 160000 + + +.1 < . 1:1(0) ack 42001 win 514 + +.1 < . 1:1(0) ack 44001 win 514 + +.1 < . 1:1(0) ack 46001 win 514 + +.1 < . 1:1(0) ack 48001 win 514 + +.1 < . 1:1(0) ack 50001 win 514 + +.1 < . 1:1(0) ack 52001 win 514 + +.1 < . 1:1(0) ack 54001 win 514 + +.1 < . 1:1(0) ack 56001 win 514 + +.1 < . 1:1(0) ack 58001 win 514 + +.1 < . 1:1(0) ack 60001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 48, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 62001 win 514 + +.1 < . 1:1(0) ack 64001 win 514 + +.1 < . 1:1(0) ack 66001 win 514 + +.1 < . 1:1(0) ack 68001 win 514 + +.1 < . 1:1(0) ack 70001 win 514 + +.1 < . 1:1(0) ack 72001 win 514 + +.1 < . 1:1(0) ack 74001 win 514 + +.1 < . 1:1(0) ack 76001 win 514 + +.1 < . 1:1(0) ack 78001 win 514 + +.1 < . 1:1(0) ack 80001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 49, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 82001 win 514 + +.1 < . 1:1(0) ack 84001 win 514 + +.1 < . 1:1(0) ack 86001 win 514 + +.1 < . 1:1(0) ack 88001 win 514 + +.1 < . 1:1(0) ack 90001 win 514 + +.1 < . 1:1(0) ack 92001 win 514 + +.1 < . 1:1(0) ack 94001 win 514 + +.1 < . 1:1(0) ack 96001 win 514 + +.1 < . 1:1(0) ack 98001 win 514 + +.1 < . 1:1(0) ack 100001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 52, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 102001 win 514 + +.1 < . 1:1(0) ack 104001 win 514 + +.1 < . 1:1(0) ack 106001 win 514 + +.1 < . 1:1(0) ack 108001 win 514 + +.1 < . 1:1(0) ack 110001 win 514 + +.1 < . 1:1(0) ack 112001 win 514 + +.1 < . 1:1(0) ack 114001 win 514 + +.1 < . 1:1(0) ack 116001 win 514 + +.1 < . 1:1(0) ack 118001 win 514 + +.1 < . 1:1(0) ack 120001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 58, tcpi_snd_cwnd }% diff --git a/gtests/net/tcp/cubic/cubic-bulk-166k.pkt b/gtests/net/tcp/cubic/cubic-bulk-166k.pkt new file mode 100644 index 00000000..4023c617 --- /dev/null +++ b/gtests/net/tcp/cubic/cubic-bulk-166k.pkt @@ -0,0 +1,287 @@ +// Test bulk CUBIC flow at 166 kbit/sec (2 1000-byte payloads each 100ms +// means 2 * 1040 * 8 / .1 = 166400 bits/sec). +// Verifies that CUBIC increases its cwnd by at most 1 packet +// for every 2 packets ACKed. +// Also tests the CUBIC Hystart algorithm for exiting slow start. + +// For this test We mostly just care about cwnd values, not exact timing. +// So to reduce flakes allow ~20ms of timing variation: +--tolerance_usecs=20000 + +`../../common/defaults.sh +tc qdisc replace dev tun0 root fq +sysctl -q net.ipv4.tcp_congestion_control=cubic +sysctl -q net.ipv4.tcp_min_tso_segs=4 +# Disable TLP to avoid flakes from spurious retransmits: +sysctl -q net.ipv4.tcp_early_retrans=0` + +// Initialize connection + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + + +0 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 + +.1 < . 1:1(0) ack 1 win 514 + + +0 accept(3, ..., ...) = 4 + +// SO_SNDBUF is doubled, so we can write the full amount below: + +0 setsockopt(4, SOL_SOCKET, SO_SNDBUF, [200000], 4) = 0 + +0 %{ assert tcpi_snd_cwnd == 10, tcpi_snd_cwnd }% + +0 write(4, ..., 400000) = 400000 + + +.1 < . 1:1(0) ack 2001 win 514 + +.1 < . 1:1(0) ack 4001 win 514 + +.1 < . 1:1(0) ack 6001 win 514 + +.1 < . 1:1(0) ack 8001 win 514 + +.1 < . 1:1(0) ack 10001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 20, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 12001 win 514 + +.1 < . 1:1(0) ack 14001 win 514 + +.1 < . 1:1(0) ack 16001 win 514 + +.1 < . 1:1(0) ack 18001 win 514 + +.1 < . 1:1(0) ack 20001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 30, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 22001 win 514 + +.1 < . 1:1(0) ack 24001 win 514 + +.1 < . 1:1(0) ack 26001 win 514 + +.1 < . 1:1(0) ack 28001 win 514 + +.1 < . 1:1(0) ack 30001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 40, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 32001 win 514 + +.1 < . 1:1(0) ack 34001 win 514 + +.1 < . 1:1(0) ack 36001 win 514 + +.1 < . 1:1(0) ack 38001 win 514 +// Hystart exits slow start here at a cwnd of 48: + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 48, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 40001 win 514 + +.1 < . 1:1(0) ack 42001 win 514 + +.1 < . 1:1(0) ack 44001 win 514 + +.1 < . 1:1(0) ack 46001 win 514 + +.1 < . 1:1(0) ack 48001 win 514 + +.1 < . 1:1(0) ack 50001 win 514 + +.1 < . 1:1(0) ack 52001 win 514 + +.1 < . 1:1(0) ack 54001 win 514 + +.1 < . 1:1(0) ack 56001 win 514 + +.1 < . 1:1(0) ack 58001 win 514 + +.1 < . 1:1(0) ack 60001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 49, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 62001 win 514 + +.1 < . 1:1(0) ack 64001 win 514 + +.1 < . 1:1(0) ack 66001 win 514 + +.1 < . 1:1(0) ack 68001 win 514 + +.1 < . 1:1(0) ack 70001 win 514 + +.1 < . 1:1(0) ack 72001 win 514 + +.1 < . 1:1(0) ack 74001 win 514 + +.1 < . 1:1(0) ack 76001 win 514 + +.1 < . 1:1(0) ack 78001 win 514 + +.1 < . 1:1(0) ack 80001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 50, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 82001 win 514 + +.1 < . 1:1(0) ack 84001 win 514 + +.1 < . 1:1(0) ack 86001 win 514 + +.1 < . 1:1(0) ack 88001 win 514 + +.1 < . 1:1(0) ack 90001 win 514 + +.1 < . 1:1(0) ack 92001 win 514 + +.1 < . 1:1(0) ack 94001 win 514 + +.1 < . 1:1(0) ack 96001 win 514 + +.1 < . 1:1(0) ack 98001 win 514 + +.1 < . 1:1(0) ack 100001 win 514 + +0 %{ print(tcpi_snd_cwnd); assert tcpi_snd_cwnd == 52, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 102001 win 514 + +.1 < . 1:1(0) ack 104001 win 514 + +.1 < . 1:1(0) ack 106001 win 514 + +.1 < . 1:1(0) ack 108001 win 514 + +.1 < . 1:1(0) ack 110001 win 514 + +.1 < . 1:1(0) ack 112001 win 514 + +.1 < . 1:1(0) ack 114001 win 514 + +.1 < . 1:1(0) ack 116001 win 514 + +.1 < . 1:1(0) ack 118001 win 514 + +.1 < . 1:1(0) ack 120001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd = 58; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + +// From this point onward, we verify that for every 2 packets ACKed, +// CUBIC increases its cwnd by 1 packet. + +.1 < . 1:1(0) ack 122001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 1; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 124001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 1; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 126001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 1; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 128001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 1; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 130001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 1; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 132001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 1; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 134001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 1; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 136001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 1; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 138001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 1; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + +.1 < . 1:1(0) ack 140001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 1; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 142001 win 514 + +.1 < . 1:1(0) ack 144001 win 514 + +.1 < . 1:1(0) ack 146001 win 514 + +.1 < . 1:1(0) ack 148001 win 514 + +.1 < . 1:1(0) ack 150001 win 514 + +.1 < . 1:1(0) ack 152001 win 514 + +.1 < . 1:1(0) ack 154001 win 514 + +.1 < . 1:1(0) ack 156001 win 514 + +.1 < . 1:1(0) ack 158001 win 514 + +.1 < . 1:1(0) ack 160001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 162001 win 514 + +.1 < . 1:1(0) ack 164001 win 514 + +.1 < . 1:1(0) ack 166001 win 514 + +.1 < . 1:1(0) ack 168001 win 514 + +.1 < . 1:1(0) ack 170001 win 514 + +.1 < . 1:1(0) ack 172001 win 514 + +.1 < . 1:1(0) ack 174001 win 514 + +.1 < . 1:1(0) ack 176001 win 514 + +.1 < . 1:1(0) ack 178001 win 514 + +.1 < . 1:1(0) ack 180001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 182001 win 514 + +.1 < . 1:1(0) ack 184001 win 514 + +.1 < . 1:1(0) ack 186001 win 514 + +.1 < . 1:1(0) ack 188001 win 514 + +.1 < . 1:1(0) ack 190001 win 514 + +.1 < . 1:1(0) ack 192001 win 514 + +.1 < . 1:1(0) ack 194001 win 514 + +.1 < . 1:1(0) ack 196001 win 514 + +.1 < . 1:1(0) ack 198001 win 514 + +.1 < . 1:1(0) ack 200001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 202001 win 514 + +.1 < . 1:1(0) ack 204001 win 514 + +.1 < . 1:1(0) ack 206001 win 514 + +.1 < . 1:1(0) ack 208001 win 514 + +.1 < . 1:1(0) ack 210001 win 514 + +.1 < . 1:1(0) ack 212001 win 514 + +.1 < . 1:1(0) ack 214001 win 514 + +.1 < . 1:1(0) ack 216001 win 514 + +.1 < . 1:1(0) ack 218001 win 514 + +.1 < . 1:1(0) ack 220001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 222001 win 514 + +.1 < . 1:1(0) ack 224001 win 514 + +.1 < . 1:1(0) ack 226001 win 514 + +.1 < . 1:1(0) ack 228001 win 514 + +.1 < . 1:1(0) ack 230001 win 514 + +.1 < . 1:1(0) ack 232001 win 514 + +.1 < . 1:1(0) ack 234001 win 514 + +.1 < . 1:1(0) ack 236001 win 514 + +.1 < . 1:1(0) ack 238001 win 514 + +.1 < . 1:1(0) ack 240001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 242001 win 514 + +.1 < . 1:1(0) ack 244001 win 514 + +.1 < . 1:1(0) ack 246001 win 514 + +.1 < . 1:1(0) ack 248001 win 514 + +.1 < . 1:1(0) ack 250001 win 514 + +.1 < . 1:1(0) ack 252001 win 514 + +.1 < . 1:1(0) ack 254001 win 514 + +.1 < . 1:1(0) ack 256001 win 514 + +.1 < . 1:1(0) ack 258001 win 514 + +.1 < . 1:1(0) ack 260001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 262001 win 514 + +.1 < . 1:1(0) ack 264001 win 514 + +.1 < . 1:1(0) ack 266001 win 514 + +.1 < . 1:1(0) ack 268001 win 514 + +.1 < . 1:1(0) ack 270001 win 514 + +.1 < . 1:1(0) ack 272001 win 514 + +.1 < . 1:1(0) ack 274001 win 514 + +.1 < . 1:1(0) ack 276001 win 514 + +.1 < . 1:1(0) ack 278001 win 514 + +.1 < . 1:1(0) ack 280001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 282001 win 514 + +.1 < . 1:1(0) ack 284001 win 514 + +.1 < . 1:1(0) ack 286001 win 514 + +.1 < . 1:1(0) ack 288001 win 514 + +.1 < . 1:1(0) ack 290001 win 514 + +.1 < . 1:1(0) ack 292001 win 514 + +.1 < . 1:1(0) ack 294001 win 514 + +.1 < . 1:1(0) ack 296001 win 514 + +.1 < . 1:1(0) ack 298001 win 514 + +.1 < . 1:1(0) ack 300001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 302001 win 514 + +.1 < . 1:1(0) ack 304001 win 514 + +.1 < . 1:1(0) ack 306001 win 514 + +.1 < . 1:1(0) ack 308001 win 514 + +.1 < . 1:1(0) ack 310001 win 514 + +.1 < . 1:1(0) ack 312001 win 514 + +.1 < . 1:1(0) ack 314001 win 514 + +.1 < . 1:1(0) ack 316001 win 514 + +.1 < . 1:1(0) ack 318001 win 514 + +.1 < . 1:1(0) ack 320001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 322001 win 514 + +.1 < . 1:1(0) ack 324001 win 514 + +.1 < . 1:1(0) ack 326001 win 514 + +.1 < . 1:1(0) ack 328001 win 514 + +.1 < . 1:1(0) ack 330001 win 514 + +.1 < . 1:1(0) ack 332001 win 514 + +.1 < . 1:1(0) ack 334001 win 514 + +.1 < . 1:1(0) ack 336001 win 514 + +.1 < . 1:1(0) ack 338001 win 514 + +.1 < . 1:1(0) ack 340001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 342001 win 514 + +.1 < . 1:1(0) ack 344001 win 514 + +.1 < . 1:1(0) ack 346001 win 514 + +.1 < . 1:1(0) ack 348001 win 514 + +.1 < . 1:1(0) ack 350001 win 514 + +.1 < . 1:1(0) ack 352001 win 514 + +.1 < . 1:1(0) ack 354001 win 514 + +.1 < . 1:1(0) ack 356001 win 514 + +.1 < . 1:1(0) ack 358001 win 514 + +.1 < . 1:1(0) ack 360001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 362001 win 514 + +.1 < . 1:1(0) ack 364001 win 514 + +.1 < . 1:1(0) ack 366001 win 514 + +.1 < . 1:1(0) ack 368001 win 514 + +.1 < . 1:1(0) ack 370001 win 514 + +.1 < . 1:1(0) ack 372001 win 514 + +.1 < . 1:1(0) ack 374001 win 514 + +.1 < . 1:1(0) ack 376001 win 514 + +.1 < . 1:1(0) ack 378001 win 514 + +.1 < . 1:1(0) ack 380001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 382001 win 514 + +.1 < . 1:1(0) ack 384001 win 514 + +.1 < . 1:1(0) ack 386001 win 514 + +.1 < . 1:1(0) ack 388001 win 514 + +.1 < . 1:1(0) ack 390001 win 514 + +.1 < . 1:1(0) ack 392001 win 514 + +.1 < . 1:1(0) ack 394001 win 514 + +.1 < . 1:1(0) ack 396001 win 514 + +.1 < . 1:1(0) ack 398001 win 514 + +.1 < . 1:1(0) ack 400001 win 514 + +0 %{ print(tcpi_snd_cwnd); cwnd += 10; assert tcpi_snd_cwnd == cwnd, tcpi_snd_cwnd }% diff --git a/gtests/net/tcp/cubic/cubic-hystart-delay-min-rtt-jumps-downward.pkt b/gtests/net/tcp/cubic/cubic-hystart-delay-min-rtt-jumps-downward.pkt new file mode 100644 index 00000000..65087739 --- /dev/null +++ b/gtests/net/tcp/cubic/cubic-hystart-delay-min-rtt-jumps-downward.pkt @@ -0,0 +1,103 @@ +// Test of the CUBIC HYSTART_DELAY mechanism. +// Verify that RTT samples after the 8th sample in a round +// trip that reduce the min_rtt substantially do not cause +// Hystart to spuriously exit slow start. +// Tests: b344579ca8478598937215f7005d6c7b84d28aee (2020-06-24) +// "tcp_cubic: fix spurious HYSTART_DELAY exit upon drop in min RTT" + +`../common/defaults.sh +echo 3 > /sys/module/tcp_cubic/parameters/hystart_detect +` + + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + + +0 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 ++.200 < . 1:1(0) ack 1 win 257 + +0 accept(3, ..., ...) = 4 + +0 setsockopt(4, SOL_SOCKET, SO_SNDBUF, [200000], 4) = 0 + + +0 %{ TCP_INFINITE_SSTHRESH = 0x7fffffff }% + + +0 write(4, ..., 28000) = 28000 + +0 > P. 1:10001(10000) ack 1 + +0 %{ assert tcpi_snd_cwnd == 10, tcpi_snd_cwnd }% + ++.200 < . 1:1(0) ack 10001 win 257 + +0 > P. 10001:28001(18000) ack 1 + +0 %{ assert tcpi_snd_cwnd == 20, tcpi_snd_cwnd }% + +// Now we make a small write to allow a simple case +// where the RTT goes down, without causing a spurious retransmit. ++.180 write(4, ..., 2000) = 2000 + +0 > P. 28001:30001(2000) ack 1 + +// Now cwnd is above hystart_low_window, so Hystart is in effect. +// In bictcp_cong_avoid() the code marks this as the start of +// a new round trip, and resets sample_cnt to 0. +// This RTT sample is 200ms, and the following 9 RTT samples +// are each 1ms higher than the previous. None are high enough +// to reach the Hystart delay threshold of 1.125x higher than +// the min_rtt of 200ms, or 225ms. ++.020 < . 1:1(0) ack 12001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 22, tcpi_snd_cwnd }% + ++.001 < . 1:1(0) ack 14001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 24, tcpi_snd_cwnd }% +// Now sample_cnt is 1. + ++.001 < . 1:1(0) ack 16001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 26, tcpi_snd_cwnd }% +// Now sample_cnt is 2. + ++.001 < . 1:1(0) ack 18001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 28, tcpi_snd_cwnd }% +// Now sample_cnt is 3. + ++.001 < . 1:1(0) ack 20001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 30, tcpi_snd_cwnd }% +// Now sample_cnt is 4. + ++.001 < . 1:1(0) ack 22001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 32, tcpi_snd_cwnd }% +// Now sample_cnt is 5. + ++.001 < . 1:1(0) ack 24001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 34, tcpi_snd_cwnd }% +// Now sample_cnt is 6. + ++.001 < . 1:1(0) ack 26001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 36, tcpi_snd_cwnd }% +// Now sample_cnt is 7. + ++.001 < . 1:1(0) ack 28001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 38, tcpi_snd_cwnd }% +// Now sample_cnt is 8. + ++.001 < . 1:1(0) ack 30001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 40, tcpi_snd_cwnd }% +// Now sample_cnt is 9, which is greater than HYSTART_MIN_SAMPLES (8), +// so Hystart makes its first delay check. The RTT sample here is +// 20ms + 9*1ms = 29ms, which is a new minimum. So the minimum RTT +// has gone down. +// +// Before the Jun 2020 Hystart bug fix, Hystart would fail to incorporate +// this new min RTT in curr_rtt, and so would compare +// a curr_rtt ~= 201ms with a delay_min of 29ms, and spuriously +// exit slow start on an ACK where the min RTT went down. +// +// With the Jun 2020 Hystart bug fix, Hystart would *correctly* incorporate +// this new min RTT in curr_rtt, and so would not exit slow start. diff --git a/gtests/net/tcp/cubic/cubic-hystart-delay-rtt-jumps-upward.pkt b/gtests/net/tcp/cubic/cubic-hystart-delay-rtt-jumps-upward.pkt new file mode 100644 index 00000000..e62b30cf --- /dev/null +++ b/gtests/net/tcp/cubic/cubic-hystart-delay-rtt-jumps-upward.pkt @@ -0,0 +1,84 @@ +// Test of the CUBIC HYSTART_DELAY mechanism. +// Verify that it correctly exits slow start if there are more than +// 8 RTT samples in a round trip that are >= 12.5% higher than +// the connection's min_rtt. + +`../common/defaults.sh +echo 3 > /sys/module/tcp_cubic/parameters/hystart_detect +` + + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + + +0 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 ++.200 < . 1:1(0) ack 1 win 257 + +0 accept(3, ..., ...) = 4 + +0 setsockopt(4, SOL_SOCKET, SO_SNDBUF, [200000], 4) = 0 + + +0 %{ TCP_INFINITE_SSTHRESH = 0x7fffffff }% + + +0 write(4, ..., 30000) = 30000 + +0 > P. 1:10001(10000) ack 1 + +0 %{ assert tcpi_snd_cwnd == 10, tcpi_snd_cwnd }% + ++.200 < . 1:1(0) ack 10001 win 257 + +0 > P. 10001:30001(20000) ack 1 + +0 %{ assert tcpi_snd_cwnd == 20, tcpi_snd_cwnd }% + +// Now cwnd is above hystart_low_window, so Hystart is in effect. +// In hystart_update() the code marks this as the start of +// a new round trip, and resets sample_cnt to 0, then increments +// sample_cnt to 1. ++.240 < . 1:1(0) ack 12001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 22, tcpi_snd_cwnd }% + ++.001 < . 1:1(0) ack 14001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 24, tcpi_snd_cwnd }% +// Now sample_cnt is 2. + ++.001 < . 1:1(0) ack 16001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 26, tcpi_snd_cwnd }% +// Now sample_cnt is 3. + ++.001 < . 1:1(0) ack 18001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 28, tcpi_snd_cwnd }% +// Now sample_cnt is 4. + ++.001 < . 1:1(0) ack 20001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 30, tcpi_snd_cwnd }% +// Now sample_cnt is 5. + ++.001 < . 1:1(0) ack 22001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 32, tcpi_snd_cwnd }% +// Now sample_cnt is 6. + ++.001 < . 1:1(0) ack 24001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 34, tcpi_snd_cwnd }% +// Now sample_cnt is 7. + ++.001 < . 1:1(0) ack 26001 win 257 + +0 %{ assert tcpi_snd_ssthresh == TCP_INFINITE_SSTHRESH, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 36, tcpi_snd_cwnd }% +// Now sample_cnt is 8. + ++.001 < . 1:1(0) ack 28001 win 257 +// Now sample_cnt is 9, which is > HYSTART_MIN_SAMPLES (8), +// so Hystart makes its first delay check. The min RTT sample in this +// round trip is >= 1.125 x min_rtt = 225ms. +// So Hystart will exit slow start and set ssthresh to the current cwnd. + +0 %{ assert tcpi_snd_ssthresh == 36, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 36, tcpi_snd_cwnd }% + ++.001 < . 1:1(0) ack 30001 win 257 + +0 %{ assert tcpi_snd_ssthresh == 36, tcpi_snd_ssthresh }% + +0 %{ assert tcpi_snd_cwnd == 36, tcpi_snd_cwnd }% diff --git a/gtests/net/tcp/cubic/cubic-rack-reo-timeout-retrans-failed-incoming-data.pkt b/gtests/net/tcp/cubic/cubic-rack-reo-timeout-retrans-failed-incoming-data.pkt new file mode 100644 index 00000000..bcf493e7 --- /dev/null +++ b/gtests/net/tcp/cubic/cubic-rack-reo-timeout-retrans-failed-incoming-data.pkt @@ -0,0 +1,51 @@ +// Check behavior in an edge case: RACK reorder timeout, retransmit fails, +// then incoming data arrives. +// Based on bbr-rack-reo-timeout-retrans-failed-incoming-data.pkt + +`../../common/defaults.sh` + + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + + +0 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 <...> ++.020 < . 1:1(0) ack 1 win 320 + +0 accept(3, ..., ...) = 4 + + +0 write(4, ..., 10000) = 10000 + +0 > P. 1:10001(10000) ack 1 +// Short-time-scale reordering causes some SACKs: ++.020 < . 1:1(0) ack 1 win 320 + +0 < . 1:1(0) ack 1 win 320 + +0 %{ assert tcpi_sacked == 2, tcpi_sacked}% + +0 < . 1:1(0) ack 10001 win 320 + +0 %{ assert tcpi_sacked == 0, tcpi_sacked}% ++.010 %{ assert tcpi_reordering == 10, tcpi_reordering }% + +0 %{ assert tcpi_reord_seen == 2 }% // 2 cases of reordering ++.010 %{ assert tcpi_snd_cwnd == 19, tcpi_snd_cwnd }% + ++.005 write(4, ..., 2000) = 2000 ++.000 > P. 10001:12001(2000) ack 1 + +// Emulate qdisc congestion: no packets can get out now. ++0 `tc qdisc replace dev tun0 root pfifo limit 0` ++.020 < . 1:1(0) ack 10001 win 320 + +0 %{ assert tcpi_ca_state == TCP_CA_Disorder, tcpi_ca_state }% + +0 %{ assert tcpi_snd_cwnd == 19, tcpi_snd_cwnd }% + +// RACK reordering timer fires and we try to retransmit 10001:11001, but can't, +// due to the local qdisc congestion. ++.020 %{ assert tcpi_ca_state == TCP_CA_Recovery, 'ca_state: %d min_rtt: %d rtt: %d' % (tcpi_ca_state, tcpi_min_rtt, tcpi_rtt) }% + +0 %{ assert tcpi_unacked == 2, tcpi_unacked }% + +0 %{ assert tcpi_sacked == 1, tcpi_sacked }% + +0 %{ assert tcpi_lost == 1, tcpi_lost }% + +0 %{ assert tcpi_retrans == 0, tcpi_retrans }% // retransmit failed! + +0 %{ assert tcpi_snd_cwnd == 1, tcpi_snd_cwnd }% +// No packets are in flight + +// An ACK in Recovery with no packets in flight, and no packets ACKed. +// Look out! ++.010 < . 1:1001(1000) ack 10001 win 320 + +0 %{ assert tcpi_snd_cwnd == 1, tcpi_snd_cwnd }% diff --git a/gtests/net/tcp/cubic/cubic-rto-ss-ca-cwnd-bump.pkt b/gtests/net/tcp/cubic/cubic-rto-ss-ca-cwnd-bump.pkt new file mode 100644 index 00000000..46e8892d --- /dev/null +++ b/gtests/net/tcp/cubic/cubic-rto-ss-ca-cwnd-bump.pkt @@ -0,0 +1,111 @@ +// Test CUBIC in the following scenario: +// RTO, then slow start, then congestion avoidance. +// Verify that during congestion avoidance after slow start, +// sender increments cwnd by 1 per 20 packets ACKed. +// Receiver is ACKing every packet, to make the math easy to follow. +// + +// Set up config. +`../common/defaults.sh` + + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + + +0 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 + +.1 < . 1:1(0) ack 1 win 257 + +0 accept(3, ..., ...) = 4 + +0 setsockopt(4, SOL_SOCKET, SO_SNDBUF, [200000], 4) = 0 + +// Send out a data segment and wait for the ACK, so we get an RTT sample +// in addition to the three-way-handshake RTT sample. + +0 write(4, ..., 1000) = 1000 + +0 > P. 1:1001(1000) ack 1 + +.1 < . 1:1(0) ack 1001 win 257 + +0 %{ assert tcpi_snd_cwnd == 10, tcpi_snd_cwnd }% // not cwnd-limited... + +// Send out a second packet. + +0 write(4, ..., 1000) = 1000 + +0 > P. 1001:2001(1000) ack 1 + +// RTO is shorter, so TLP scheduled in its place. + +.3~+.303 > P. 1001:2001(1000) ack 1 + +// RTO at srtt + rttvar ~= 100 + 2*100. +// See tcp_input.c comment: "make sure rto = 3*rtt". + +.3~+.303 > P. 1001:2001(1000) ack 1 + +// ACK for retransmission. + +.1 < . 1:1(0) ack 2001 win 257 + +0 %{ +assert tcpi_snd_cwnd == 2, tcpi_snd_cwnd +assert tcpi_snd_ssthresh == 7, tcpi_snd_ssthresh +}% + +// Now we're in slow start and will grow cwnd exponentially until it +// passes the ssthresh of 7. + + +0 write(4, ..., 200000) = 200000 + +0 > . 2001:4001(2000) ack 1 + +0 %{ assert tcpi_snd_cwnd == 2,tcpi_snd_cwnd }% + + +.1 < . 1:1(0) ack 3001 win 257 + +0 > . 4001:6001(2000) ack 1 + +0 %{ assert tcpi_snd_cwnd == 3,tcpi_snd_cwnd }% + ++.002 < . 1:1(0) ack 4001 win 257 + +0 > P. 6001:8001(2000) ack 1 + +0 %{ assert tcpi_snd_cwnd == 4,tcpi_snd_cwnd }% + ++.098 < . 1:1(0) ack 5001 win 257 + +0 > P. 8001:10001(2000) ack 1 + +0 %{ assert tcpi_snd_cwnd == 5,tcpi_snd_cwnd }% + ++.002 < . 1:1(0) ack 6001 win 257 + +0 > P. 10001:12001(2000) ack 1 + +0 %{ assert tcpi_snd_cwnd == 6,tcpi_snd_cwnd }% + ++.002 < . 1:1(0) ack 7001 win 257 + +0 > P. 12001:14001(2000) ack 1 + +0 %{ assert tcpi_snd_cwnd == 7,tcpi_snd_cwnd }% + +// Now we're in congestion avoidance and we'll increase +// cwnd until we plateau at the cwnd value at which we +// experienced the loss (Wmax of 10 packets). ++.002 < . 1:1(0) ack 8001 win 257 + +0 > . 14001:15001(1000) ack 1 ++.098 < . 1:1(0) ack 9001 win 257 + +0 > . 15001:16001(1000) ack 1 ++.002 < . 1:1(0) ack 10001 win 257 + +0 > . 16001:17001(1000) ack 1 ++.002 < . 1:1(0) ack 11001 win 257 + +0 > . 17001:18001(1000) ack 1 ++.002 < . 1:1(0) ack 12001 win 257 + +0 > . 18001:19001(1000) ack 1 ++.002 < . 1:1(0) ack 13001 win 257 + +0 > P. 19001:20001(1000) ack 1 ++.002 < . 1:1(0) ack 14001 win 257 + +0 > . 20001:21001(1000) ack 1 ++.002 < . 1:1(0) ack 15001 win 257 + +0 > . 21001:22001(1000) ack 1 ++.098 < . 1:1(0) ack 16001 win 257 + +0 > . 22001:23001(1000) ack 1 ++.001 %{ assert tcpi_snd_cwnd == 7,tcpi_snd_cwnd }% + ++.002 < . 1:1(0) ack 17001 win 257 + +0 > . 23001:24001(1000) ack 1 ++.002 < . 1:1(0) ack 18001 win 257 + +0 > . 24001:25001(1000) ack 1 ++.002 < . 1:1(0) ack 19001 win 257 + +0 > . 25001:26001(1000) ack 1 ++.002 < . 1:1(0) ack 20001 win 257 + +0 > . 26001:27001(1000) ack 1 ++.002 < . 1:1(0) ack 21001 win 257 + +0 > . 27001:28001(1000) ack 1 ++.002 < . 1:1(0) ack 22001 win 257 + +0 > . 28001:29001(1000) ack 1 ++.098 < . 1:1(0) ack 23001 win 257 + +0 > P. 29001:31001(2000) ack 1 + +0 %{ assert tcpi_snd_cwnd == 8,tcpi_snd_cwnd }%