aboutsummaryrefslogtreecommitdiffstats
path: root/test/packetdrill/tests/linux
diff options
context:
space:
mode:
Diffstat (limited to 'test/packetdrill/tests/linux')
-rw-r--r--test/packetdrill/tests/linux/README7
-rw-r--r--test/packetdrill/tests/linux/blocking/blocking-accept.pkt15
-rw-r--r--test/packetdrill/tests/linux/blocking/blocking-read.pkt25
-rw-r--r--test/packetdrill/tests/linux/close/close-read-data-fin.pkt38
-rw-r--r--test/packetdrill/tests/linux/close/close-so-linger-onoff-1-linger-0-rst.pkt28
-rw-r--r--test/packetdrill/tests/linux/close/close-unread-data-rst.pkt38
-rw-r--r--test/packetdrill/tests/linux/connect/http-get-nonblocking-ts.pkt34
-rw-r--r--test/packetdrill/tests/linux/early_retransmit/er-delayed-2pkt-sack.pkt27
-rw-r--r--test/packetdrill/tests/linux/early_retransmit/er-delayed-3pkt-sack.pkt28
-rw-r--r--test/packetdrill/tests/linux/early_retransmit/er-delayed-filled-3pkt-sack.pkt31
-rw-r--r--test/packetdrill/tests/linux/early_retransmit/er-delayed-get-ack-3pkt-sack.pkt35
-rw-r--r--test/packetdrill/tests/linux/early_retransmit/er-quick-2pkt-sack.pkt27
-rw-r--r--test/packetdrill/tests/linux/early_retransmit/er-quick-3pkt-sack.pkt28
-rw-r--r--test/packetdrill/tests/linux/fast_recovery/prr-ss-ack-below-snd_una-reno.pkt51
-rw-r--r--test/packetdrill/tests/linux/fast_retransmit/fr-4pkt-sack-linux.pkt35
-rw-r--r--test/packetdrill/tests/linux/icmp/icmp-all-types.pkt71
-rw-r--r--test/packetdrill/tests/linux/inet_diag/inet-diag-ipv4-mapped-ipv6.pkt29
-rw-r--r--test/packetdrill/tests/linux/inet_diag/inet-diag-ipv4.pkt28
-rw-r--r--test/packetdrill/tests/linux/inet_diag/inet-diag-ipv6.pkt29
-rw-r--r--test/packetdrill/tests/linux/init_rto/init_rto_passive_open.pkt17
-rwxr-xr-xtest/packetdrill/tests/linux/initial_window/iw10-base-case.pkt21
-rwxr-xr-xtest/packetdrill/tests/linux/initial_window/iw10-short-response.pkt21
-rw-r--r--test/packetdrill/tests/linux/ioctl/ioctl-siocinq-fin.pkt30
-rw-r--r--test/packetdrill/tests/linux/listen/listen-incoming-ack.pkt20
-rw-r--r--test/packetdrill/tests/linux/listen/listen-incoming-no-tcp-flags.pkt21
-rw-r--r--test/packetdrill/tests/linux/listen/listen-incoming-rst.pkt22
-rw-r--r--test/packetdrill/tests/linux/listen/listen-incoming-syn-ack.pkt20
-rw-r--r--test/packetdrill/tests/linux/listen/listen-incoming-syn-rst.pkt22
-rw-r--r--test/packetdrill/tests/linux/listen/listen-unbound.pkt5
-rw-r--r--test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-client-ts.pkt17
-rw-r--r--test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-client.pkt14
-rw-r--r--test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-advmss-ipv4.pkt29
-rw-r--r--test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-advmss-ts-ipv4.pkt30
-rw-r--r--test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-ts.pkt20
-rw-r--r--test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server.pkt17
-rw-r--r--test/packetdrill/tests/linux/mss/mss-setsockopt-tcp_maxseg-client.pkt24
-rw-r--r--test/packetdrill/tests/linux/mss/mss-setsockopt-tcp_maxseg-server.pkt27
-rw-r--r--test/packetdrill/tests/linux/pmtu_discovery/pmtud-10pkt-1460-to-1160.pkt54
-rw-r--r--test/packetdrill/tests/linux/pmtu_discovery/pmtud-1pkt-1460-to-1160.pkt36
-rw-r--r--test/packetdrill/tests/linux/receiver_rtt/rcv-rtt-with-timestamps-new.pkt57
-rw-r--r--test/packetdrill/tests/linux/receiver_rtt/rcv-rtt-without-timestamps-new.pkt62
-rwxr-xr-xtest/packetdrill/tests/linux/run_tests.sh6
-rw-r--r--test/packetdrill/tests/linux/sack/sack-shift-sacked-1-2-3-fack.pkt47
-rw-r--r--test/packetdrill/tests/linux/sack/sack-shift-sacked-1-2:6-fack.pkt39
-rw-r--r--test/packetdrill/tests/linux/shutdown/shutdown-rd-close.pkt29
-rw-r--r--test/packetdrill/tests/linux/shutdown/shutdown-rd-wr-close.pkt45
-rw-r--r--test/packetdrill/tests/linux/shutdown/shutdown-rdwr-close.pkt26
-rw-r--r--test/packetdrill/tests/linux/shutdown/shutdown-wr-close.pkt29
-rw-r--r--test/packetdrill/tests/linux/undo/undo-fr-ack-then-dsack-on-ack-below-snd_una.pkt55
-rw-r--r--test/packetdrill/tests/linux/undo/undo-fr-acks-dropped-then-dsack.pkt44
50 files changed, 1510 insertions, 0 deletions
diff --git a/test/packetdrill/tests/linux/README b/test/packetdrill/tests/linux/README
new file mode 100644
index 0000000..0e6db19
--- /dev/null
+++ b/test/packetdrill/tests/linux/README
@@ -0,0 +1,7 @@
+Packetdrill tests for Linux.
+
+This directory contains Packetdrill tests for Linux. The tests all pass under
+kernel 3.11.0-rc4 (installed on an Ubuntu 13.04 machine). However, due to TCP
+metrics caching in recent kernels, a second run of all tests can result in
+failures. The script run_tests.sh in this directory uses the iproute tool to
+flush the TCP metrics cache before each test.
diff --git a/test/packetdrill/tests/linux/blocking/blocking-accept.pkt b/test/packetdrill/tests/linux/blocking/blocking-accept.pkt
new file mode 100644
index 0000000..02c7cd8
--- /dev/null
+++ b/test/packetdrill/tests/linux/blocking/blocking-accept.pkt
@@ -0,0 +1,15 @@
+// Test for blocking accept.
+
+// Establish a connection.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+0.000...0.200 accept(3, ..., ...) = 4
+
+0.100 < S 0:0(0) win 32792 <mss 1000,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+
+0.300 write(4, ..., 2000) = 2000
+0.300 > P. 1:2001(2000) ack 1
diff --git a/test/packetdrill/tests/linux/blocking/blocking-read.pkt b/test/packetdrill/tests/linux/blocking/blocking-read.pkt
new file mode 100644
index 0000000..1c734c1
--- /dev/null
+++ b/test/packetdrill/tests/linux/blocking/blocking-read.pkt
@@ -0,0 +1,25 @@
+// Test for blocking read.
+
+// Establish a connection.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1000,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+0.200...0.300 read(4, ..., 2000) = 2000
+0.300 < P. 1:2001(2000) ack 1 win 257
+0.300 > . 1:1(0) ack 2001
+
+0.400...0.500 read(4, ..., 2000) = 2000
+0.500 < P. 2001:4001(2000) ack 1 win 257
+0.500 > . 1:1(0) ack 4001
+
+0.600 < P. 4001:6001(2000) ack 1 win 257
+0.600 > . 1:1(0) ack 6001
+0.600...0.600 read(4, ..., 1000) = 1000
+0.600...0.600 read(4, ..., 1000) = 1000
diff --git a/test/packetdrill/tests/linux/close/close-read-data-fin.pkt b/test/packetdrill/tests/linux/close/close-read-data-fin.pkt
new file mode 100644
index 0000000..bad95c2
--- /dev/null
+++ b/test/packetdrill/tests/linux/close/close-read-data-fin.pkt
@@ -0,0 +1,38 @@
+// If we close the connection after read()'ing what
+// the other side sent, a FIN will be generated. This
+// behavior conforms to RFC 793.
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+// Receive first segment
+0.210 < P. 1:1001(1000) ack 1 win 46
+
+// Send one ACK
+0.210 > . 1:1(0) ack 1001
+
+// Application writes 1000 bytes
+0.250 write(4, ..., 1000) = 1000
+0.250 > P. 1:1001(1000) ack 1001
+
+// ACK
+0.300 < . 1001:1001(0) ack 1001 win 257
+
+0.400 read(4, ..., 1000) = 1000
+
+// Client closes the connection
+0.610 < F. 1001:1001(0) ack 1001 win 260
+
+// Respond with (delayed) ACK
+0.650 > . 1001:1001(0) ack 1002
+
+// We close the connection
+0.700 close(4) = 0
+0.701 > F. 1001:1001(0) ack 1002
diff --git a/test/packetdrill/tests/linux/close/close-so-linger-onoff-1-linger-0-rst.pkt b/test/packetdrill/tests/linux/close/close-so-linger-onoff-1-linger-0-rst.pkt
new file mode 100644
index 0000000..dcec1cf
--- /dev/null
+++ b/test/packetdrill/tests/linux/close/close-so-linger-onoff-1-linger-0-rst.pkt
@@ -0,0 +1,28 @@
+// Verify that when a process uses SO_LINGER with {onoff=1, linger=0},
+// and then closes the socket, the kernel sends a RST.
+// (TODO(ncardwell): it also frees the socket immediately without any
+// time in TIME_WAIT; we should test this too once we have some
+// infrastructure for testing this kind of thing reliably...)
+
+// Initialize a server socket.
+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 <mss 1000,sackOK,nop,nop,nop,wscale 7>
++0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
++0 < . 1:1(0) ack 1 win 257
+
++0 accept(3, ..., ...) = 4
+
++0 setsockopt(4, SOL_SOCKET, SO_LINGER, {onoff=1, linger=0}, 8) = 0
+
+// Write some data, receive an ACK.
++0 write(4, ..., 1000) = 1000
++0 > P. 1:1001(1000) ack 1
++0 < . 1:1(0) ack 1001 win 257
+
+// Clean up.
++0 close(4) = 0
++0 > R. 1001:1001(0) ack 1
diff --git a/test/packetdrill/tests/linux/close/close-unread-data-rst.pkt b/test/packetdrill/tests/linux/close/close-unread-data-rst.pkt
new file mode 100644
index 0000000..d30808b
--- /dev/null
+++ b/test/packetdrill/tests/linux/close/close-unread-data-rst.pkt
@@ -0,0 +1,38 @@
+// If we close the connection before read()'ing what
+// the other side sent, a RST will be generated instead
+// of a FIN.
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+// Receive first segment.
+0.210 < P. 1:1001(1000) ack 1 win 46
+
+// Send one ACK.
+0.210 > . 1:1(0) ack 1001
+
+// Application writes 1000 bytes.
+0.250 write(4, ..., 1000) = 1000
+0.250 > P. 1:1001(1000) ack 1001
+
+// ACK
+0.300 < . 1001:1001(0) ack 1001 win 257
+
+// Client closes the connection.
+0.610 < F. 1001:1001(0) ack 1001 win 260
+
+// Respond with (delayed) ACK.
+0.650 > . 1001:1001(0) ack 1002
+
+// We close the connection.
+0.700 close(4) = 0
+// Since we have not read, we generate a RST instead of a FIN
+// conforming to RFC 1122 section 4.2.2.13.
+0.701 > R. 1001:1001(0) ack 1002
diff --git a/test/packetdrill/tests/linux/connect/http-get-nonblocking-ts.pkt b/test/packetdrill/tests/linux/connect/http-get-nonblocking-ts.pkt
new file mode 100644
index 0000000..f998df4
--- /dev/null
+++ b/test/packetdrill/tests/linux/connect/http-get-nonblocking-ts.pkt
@@ -0,0 +1,34 @@
+// A simple client-side HTTP-style test that does a connect, sends a
+// short request, and receives a short response.
+
+// Create a socket and set it to non-blocking.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
+0.000 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+
+// Establish connection and verify that there was no error.
+0.100 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0.100 > S 0:0(0) <mss 1460,sackOK,TS val 100 ecr 0,nop,wscale 6>
+0.200 < S. 0:0(0) ack 1 win 5792 <mss 1460,sackOK,TS val 700 ecr 100,nop,wscale 7>
+0.200 > . 1:1(0) ack 1 <nop,nop,TS val 200 ecr 700>
+0.200 getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
+0.200 fcntl(3, F_SETFL, O_RDWR) = 0 // set back to blocking
+
+// Send the HTTP request.
+0.200 write(3, ..., 57) = 57
+0.200 > P. 1:58(57) ack 1 <nop,nop,TS val 200 ecr 700>
+0.300 < . 1:1(0) ack 58 win 92 <nop,nop,TS val 800 ecr 200>
+
+// Receive the HTTP response and the server's FIN.
+0.300 < P. 1:786(785) ack 58 win 92 <nop,nop,TS val 800 ecr 200>
+0.300 > . 58:58(0) ack 786 <nop,nop,TS val 300 ecr 800>
+0.300 < F. 786:786(0) ack 58 win 92 <nop,nop,TS val 800 ecr 200>
+0.300 read(3, ..., 1024) = 785
+0.300 read(3, ..., 1024) = 0
+// Delayed ACK.
+0.340 > . 58:58(0) ack 787 <nop,nop,TS val 300 ecr 800>
+
+// Close the connection.
+0.350 close(3) = 0
+0.350 > F. 58:58(0) ack 787 <nop,nop,TS val 300 ecr 800>
+0.450 < . 787:787(0) ack 59 win 92 <nop,nop,TS val 900 ecr 300>
diff --git a/test/packetdrill/tests/linux/early_retransmit/er-delayed-2pkt-sack.pkt b/test/packetdrill/tests/linux/early_retransmit/er-delayed-2pkt-sack.pkt
new file mode 100644
index 0000000..72afec0
--- /dev/null
+++ b/test/packetdrill/tests/linux/early_retransmit/er-delayed-2pkt-sack.pkt
@@ -0,0 +1,27 @@
+// Test delayed ER with 2 packets outstanding, receiver sending SACKs.
+
+// Enable delayed early retransmit.
+`sysctl -q net.ipv4.tcp_early_retrans=2`
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+0.200 write(4, ..., 2920) = 2920
+0.200 > P. 1:2921(2920) ack 1
+0.300 < . 1:1(0) ack 1 win 257 <sack 1461:2921,nop,nop>
+0.325 > . 1:1461(1460) ack 1 // delayed Early Retransmit at RTT/4 = 25ms
+0.425 < . 1:1(0) ack 2921 win 257
+
+0.500 close(4) = 0
+0.500 > F. 2921:2921(0) ack 1
+0.600 < F. 1:1(0) ack 2922 win 257
+0.601 > . 2922:2922(0) ack 2
+
+0.700 `sysctl -q net.ipv4.tcp_early_retrans=3`
diff --git a/test/packetdrill/tests/linux/early_retransmit/er-delayed-3pkt-sack.pkt b/test/packetdrill/tests/linux/early_retransmit/er-delayed-3pkt-sack.pkt
new file mode 100644
index 0000000..5d05264
--- /dev/null
+++ b/test/packetdrill/tests/linux/early_retransmit/er-delayed-3pkt-sack.pkt
@@ -0,0 +1,28 @@
+// Test delayed ER with 3 packets outstanding, receiver sending SACKs.
+
+// Enable delayed early retransmit.
+`sysctl -q net.ipv4.tcp_early_retrans=2`
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+0.200 write(4, ..., 4380) = 4380
+0.200 > P. 1:4381(4380) ack 1
+0.300 < . 1:1(0) ack 1 win 257 <sack 1461:2921,nop,nop>
+0.300 < . 1:1(0) ack 1 win 257 <sack 1461:4381,nop,nop>
+0.325 > . 1:1461(1460) ack 1 // delayed Early Retransmit at RTT/4 = 25ms
+0.425 < . 1:1(0) ack 4381 win 257
+
+0.500 close(4) = 0
+0.500 > F. 4381:4381(0) ack 1
+0.600 < F. 1:1(0) ack 4382 win 257
+0.601 > . 4382:4382(0) ack 2
+
+0.700 `sysctl -q net.ipv4.tcp_early_retrans=3`
diff --git a/test/packetdrill/tests/linux/early_retransmit/er-delayed-filled-3pkt-sack.pkt b/test/packetdrill/tests/linux/early_retransmit/er-delayed-filled-3pkt-sack.pkt
new file mode 100644
index 0000000..e06db1b
--- /dev/null
+++ b/test/packetdrill/tests/linux/early_retransmit/er-delayed-filled-3pkt-sack.pkt
@@ -0,0 +1,31 @@
+// Test delayed ER with 3 packets outstanding, receiver sending SACKs.
+// Added wrinkles: (1) ACK for missing first packet finally arrives,
+// filling the hole and making the ER superfluous.
+// This test verifies that the ER timer gets correctly cancelled.
+
+// Enable delayed early retransmit.
+`sysctl -q net.ipv4.tcp_early_retrans=2`
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+0.200 write(4, ..., 4380) = 4380
+0.200 > P. 1:4381(4380) ack 1
+0.300 < . 1:1(0) ack 1 win 257 <sack 1461:2921,nop,nop>
+0.300 < . 1:1(0) ack 1 win 257 <sack 1461:4381,nop,nop>
+0.310 < . 1:1(0) ack 4381 win 257
+// No ER or RTO timer should fire here, since all data is ACKed
+
+1.800 close(4) = 0
+1.800 > F. 4381:4381(0) ack 1
+1.900 < F. 1:1(0) ack 4382 win 257
+1.900 > . 4382:4382(0) ack 2
+
+2.000 `sysctl -q net.ipv4.tcp_early_retrans=3`
diff --git a/test/packetdrill/tests/linux/early_retransmit/er-delayed-get-ack-3pkt-sack.pkt b/test/packetdrill/tests/linux/early_retransmit/er-delayed-get-ack-3pkt-sack.pkt
new file mode 100644
index 0000000..794bcb0
--- /dev/null
+++ b/test/packetdrill/tests/linux/early_retransmit/er-delayed-get-ack-3pkt-sack.pkt
@@ -0,0 +1,35 @@
+// Test delayed ER with 3 packets outstanding, receiver sending SACKs.
+// Added wrinkles: (1) sender gets an ACK before delayed ER timer fires,
+// so we don't do the originally scheduled ER but instead reschedule
+// the ER timer for later.
+
+// Enable delayed early retransmit.
+`sysctl -q net.ipv4.tcp_early_retrans=2`
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+0.200 write(4, ..., 4380) = 4380
+0.200 > P. 1:4381(4380) ack 1
+0.300 < . 1:1(0) ack 1 win 257 <sack 1461:2921,nop,nop>
+0.300 < . 1:1(0) ack 1 win 257 <sack 1461:4381,nop,nop>
+// Next we get an ACK before ER fires. Any ACK should cause us
+// to cancel ER timer, whether it ACKs new data or (as in this case) does not.
+// Then we reschedule the ER timer again.
+0.310 < . 1:1(0) ack 1 win 257 <sack 1461:4381,nop,nop>
+0.335 > . 1:1461(1460) ack 1 // delayed ER at 0.310 + RTT/4=25ms
+0.435 < . 1:1(0) ack 4381 win 257
+
+0.700 close(4) = 0
+0.700 > F. 4381:4381(0) ack 1
+0.800 < F. 1:1(0) ack 4382 win 257
+0.800 > . 4382:4382(0) ack 2
+
+0.900 `sysctl -q net.ipv4.tcp_early_retrans=3`
diff --git a/test/packetdrill/tests/linux/early_retransmit/er-quick-2pkt-sack.pkt b/test/packetdrill/tests/linux/early_retransmit/er-quick-2pkt-sack.pkt
new file mode 100644
index 0000000..6d0652c
--- /dev/null
+++ b/test/packetdrill/tests/linux/early_retransmit/er-quick-2pkt-sack.pkt
@@ -0,0 +1,27 @@
+// Test quick ER (no delay) with 2 packets outstanding, receiver sending SACKs.
+
+// Enable quick early retransmit.
+`sysctl -q net.ipv4.tcp_early_retrans=1`
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+0.200 write(4, ..., 2920) = 2920
+0.200 > P. 1:2921(2920) ack 1
+0.300 < . 1:1(0) ack 1 win 257 <sack 1461:2921,nop,nop>
+0.300 > . 1:1461(1460) ack 1 // quick Early Retransmit
+0.400 < . 1:1(0) ack 2921 win 257
+
+0.500 close(4) = 0
+0.500 > F. 2921:2921(0) ack 1
+0.600 < F. 1:1(0) ack 2922 win 257
+0.601 > . 2922:2922(0) ack 2
+
+0.700 `sysctl -q net.ipv4.tcp_early_retrans=3`
diff --git a/test/packetdrill/tests/linux/early_retransmit/er-quick-3pkt-sack.pkt b/test/packetdrill/tests/linux/early_retransmit/er-quick-3pkt-sack.pkt
new file mode 100644
index 0000000..49719bb
--- /dev/null
+++ b/test/packetdrill/tests/linux/early_retransmit/er-quick-3pkt-sack.pkt
@@ -0,0 +1,28 @@
+// Test quick ER (no delay) with 3 packets outstanding, receiver sending SACKs.
+
+// Enable quick early retransmit.
+`sysctl -q net.ipv4.tcp_early_retrans=1`
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+0.200 write(4, ..., 4380) = 4380
+0.200 > P. 1:4381(4380) ack 1
+0.300 < . 1:1(0) ack 1 win 257 <sack 1461:2921,nop,nop>
+0.300 < . 1:1(0) ack 1 win 257 <sack 1461:4381,nop,nop>
+0.300 > . 1:1461(1460) ack 1 // quick ER (no delay)
+0.400 < . 1:1(0) ack 4381 win 257
+
+0.500 close(4) = 0
+0.500 > F. 4381:4381(0) ack 1
+0.600 < F. 1:1(0) ack 4382 win 257
+0.601 > . 4382:4382(0) ack 2
+
+0.700 `sysctl -q net.ipv4.tcp_early_retrans=3`
diff --git a/test/packetdrill/tests/linux/fast_recovery/prr-ss-ack-below-snd_una-reno.pkt b/test/packetdrill/tests/linux/fast_recovery/prr-ss-ack-below-snd_una-reno.pkt
new file mode 100644
index 0000000..4cc7b3b
--- /dev/null
+++ b/test/packetdrill/tests/linux/fast_recovery/prr-ss-ack-below-snd_una-reno.pkt
@@ -0,0 +1,51 @@
+// Test PRR-slowstart implementation.
+// In this variant we verify that the sender uses SACK info on an ACK
+// below snd_una.
+// This variant tests behavior with Reno congestion control.
+
+`sysctl -q net.ipv4.tcp_congestion_control=reno`
+
+// Establish a connection.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+// RTT 100ms
+0.200 < . 1:1(0) ack 1 win 320
+0.200 accept(3, ..., ...) = 4
+
+// Send 10 data segments.
+0.200 write(4, ..., 10000) = 10000
+0.200 > P. 1:10001(10000) ack 1
+
+// Lost packet 1:1001,4001:5001,7001:8001.
+// Lots of reordering in both directions.
+0.310 < . 1:1(0) ack 1 win 320 <sack 1001:2001,nop,nop>
+0.320 < . 1:1(0) ack 1 win 320 <sack 1001:3001,nop,nop>
+0.330 < . 1:1(0) ack 1 win 320 <sack 1001:3001 8001:9001,nop,nop>
+// Enter fast recovery.
+0.330 > . 1:1001(1000) ack 1
+0.330 > . 3001:4001(1000) ack 1
+
+// An ACK advances snd_una.
+0.440 < . 1:1(0) ack 4001 win 320 <sack 8001:9001,nop,nop>
+0.440 > . 4001:5001(1000) ack 1
+0.440 > . 5001:6001(1000) ack 1
+
+// The following ACK was reordered - delayed so that it arrives with
+// an ACK field below snd_una. Here we check that the newly-SACKed
+// 2MSS at 5001:7001 cause us to send out 1 more MSS.
+0.450 < . 1:1(0) ack 3001 win 320 <sack 5001:7001,nop,nop>
+0.450 > . 7001:8001(1000) ack 1
+
+// Receiver ACKs all data.
+0.560 < . 1:1(0) ack 10001 win 320
+
+// Write another 10 MSS, of which 5MSS (cwnd=ssthresh) should go out:
+0.600 write(4, ..., 10000) = 10000
+0.600 > . 10001:15001(5000) ack 1
+
+0.700 `sysctl -q net.ipv4.tcp_congestion_control=cubic`
diff --git a/test/packetdrill/tests/linux/fast_retransmit/fr-4pkt-sack-linux.pkt b/test/packetdrill/tests/linux/fast_retransmit/fr-4pkt-sack-linux.pkt
new file mode 100644
index 0000000..a1416d9
--- /dev/null
+++ b/test/packetdrill/tests/linux/fast_retransmit/fr-4pkt-sack-linux.pkt
@@ -0,0 +1,35 @@
+// Test fast retransmit with 4 packets outstanding, receiver sending SACKs.
+// In this variant the receiver supports SACK.
+
+// Establish a 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 <mss 1000,sackOK,nop,nop,nop,wscale 7>
++0 > S. 0:0(0) ack 1 <...>
+
++.1 < . 1:1(0) ack 1 win 257
++0 accept(3, ..., ...) = 4
+
+// Send 1 data segment and get an ACK, so cwnd is now 4.
++0 write(4, ..., 1000) = 1000
++0 > P. 1:1001(1000) ack 1
+
++.1 < . 1:1(0) ack 1001 win 257
+
+// Write 4 data segments.
++0 write(4, ..., 4000) = 4000
++0 > P. 1001:5001(4000) ack 1
+
+// Get 3 SACKs.
++.1 < . 1:1(0) ack 1001 win 257 <sack 2001:3001,nop,nop>
++0 < . 1:1(0) ack 1001 win 257 <sack 2001:4001,nop,nop>
++0 < . 1:1(0) ack 1001 win 257 <sack 2001:5001,nop,nop>
+// We've received 3 duplicate ACKs, so we do a fast retransmit.
++0 > . 1001:2001(1000) ack 1
+
+// Receiver ACKs all data.
++.1 < . 1:1(0) ack 6001 win 257
diff --git a/test/packetdrill/tests/linux/icmp/icmp-all-types.pkt b/test/packetdrill/tests/linux/icmp/icmp-all-types.pkt
new file mode 100644
index 0000000..169cdb4
--- /dev/null
+++ b/test/packetdrill/tests/linux/icmp/icmp-all-types.pkt
@@ -0,0 +1,71 @@
+// Test handling of incoming ICMP packets.
+// This test tests all known ICMP packet types, and a few unknown
+// types.
+
+// Establish a connection.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+// Send 1 data segment.
+0.200 write(4, ..., 1460) = 1460
+0.200 > P. 1:1461(1460) ack 1
+
+// We get lots of incoming ICMP messages.
+
+// First the unreachable type and all its codes
+0.300 < icmp unreachable net_unreachable
+0.301 < icmp unreachable host_unreachable
+0.302 < icmp unreachable protocol_unreachable
+0.303 < icmp unreachable port_unreachable
+0.304 < icmp unreachable frag_needed mtu 1234
+0.305 < icmp unreachable source_route_failed
+0.306 < icmp unreachable net_unknown
+0.307 < icmp unreachable host_unknown
+0.308 < icmp unreachable source_host_isolated
+0.309 < icmp unreachable net_prohibited
+0.310 < icmp unreachable host_prohibited
+0.311 < icmp unreachable net_unreachable_for_tos
+0.312 < icmp unreachable host_unreachable_for_tos
+0.313 < icmp unreachable packet_filtered
+0.314 < icmp unreachable precedence_violation
+0.315 < icmp unreachable precedence_cutoff
+
+// Then all the other types. These are legal because the code is optional.
+0.400 < icmp echo_reply
+0.401 < icmp source_quench
+0.402 < icmp redirect
+0.403 < icmp echo_request
+0.404 < icmp time_exceeded
+0.405 < icmp parameter_problem
+0.406 < icmp timestamp_request
+0.407 < icmp timestamp_reply
+0.408 < icmp information_request
+0.409 < icmp information_reply
+0.410 < icmp address_mask_request
+0.411 < icmp address_mask_reply
+
+// Now try symbolic types with numeric codes.
+0.450 < icmp unreachable code_0
+0.451 < icmp unreachable code_1
+0.452 < icmp unreachable code_255
+
+// Now try numeric types with numeric codes
+0.460 < icmp type_0 code_0
+0.461 < icmp type_1 code_0
+0.462 < icmp type_255 code_0
+
+// Receiver ACKs all data.
+0.470 < . 1:1(0) ack 1461 win 257
+
+// Clean up.
+0.600 close(4) = 0
+0.600 > F. 1461:1461(0) ack 1
+0.700 < F. 1:1(0) ack 1462 win 257
+0.700 > . 1462:1462(0) ack 2
diff --git a/test/packetdrill/tests/linux/inet_diag/inet-diag-ipv4-mapped-ipv6.pkt b/test/packetdrill/tests/linux/inet_diag/inet-diag-ipv4-mapped-ipv6.pkt
new file mode 100644
index 0000000..2b7d8ea
--- /dev/null
+++ b/test/packetdrill/tests/linux/inet_diag/inet-diag-ipv4-mapped-ipv6.pkt
@@ -0,0 +1,29 @@
+// Test inet_diag for AF_INET6 sockets with IPv4 traffic.
+// We use the "ss" socket statistics tool, which uses inet_diag sockets.
+// We use the default tcptest local IP address for IPv4-mapped-IPv6.
+
+// Options (command line arguments in script file) to force ipv4-mapped-ipv6.
+--ip_version="ipv4-mapped-ipv6"
+
+// Establish a 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 <mss 1000,sackOK,nop,nop,nop,wscale 2>
++0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+
++0 `ss -6 -n state SYN-RECV | grep ::ffff:192.168.0.1:8080 > /dev/null`
+
++0 < . 1:1(0) ack 1 win 32890
+
++0 accept(3, ..., ...) = 4
+
+// first, use inet_diag with no filter:
++0 `ss -6 -n | grep ::ffff:192.168.0.1:8080 > /dev/null`
+
+// then try filters, which use a different code path:
++0 `ss -6 -n --options --extended --info '( sport = :8080 )' | grep ::ffff:192.168.0.1:8080 > /dev/null`
++0 `ss -6 -n --options --extended --info '( sport = :8080 )' src ::ffff:192.168.0.1/128 | grep ::ffff:192.168.0.1:8080 > /dev/null`
diff --git a/test/packetdrill/tests/linux/inet_diag/inet-diag-ipv4.pkt b/test/packetdrill/tests/linux/inet_diag/inet-diag-ipv4.pkt
new file mode 100644
index 0000000..c4e632a
--- /dev/null
+++ b/test/packetdrill/tests/linux/inet_diag/inet-diag-ipv4.pkt
@@ -0,0 +1,28 @@
+// Test inet_diag for AF_INET sockets with IPv4 traffic.
+// We use the "ss" socket statistics tool, which uses inet_diag sockets.
+// We use the default tcptest local IP address for IPv4.
+
+// Options (command line arguments in script file) to force IPv4.
+--ip_version=ipv4
+
+// Establish a 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 <mss 1000,sackOK,nop,nop,nop,wscale 2>
++0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+
++0 `ss -4 -n state SYN-RECV | grep 192.168.0.1:8080 > /dev/null`
+
++0 < . 1:1(0) ack 1 win 32890
+
++0 accept(3, ..., ...) = 4
+
+// first, use inet_diag with no filter:
++0 `ss -4 -n | grep :8080 | grep 192.168.0.1:8080 > /dev/null`
+
+// then try filters, which use a different code path:
++0 `ss -4 -n --options --extended --info '( sport = :8080 )' | grep 192.168.0.1:8080 > /dev/null`
++0 `ss -4 -n --options --extended --info '( sport = :8080 )' src 192.168.0.1/32 | grep 192.168.0.1:8080 > /dev/null`
diff --git a/test/packetdrill/tests/linux/inet_diag/inet-diag-ipv6.pkt b/test/packetdrill/tests/linux/inet_diag/inet-diag-ipv6.pkt
new file mode 100644
index 0000000..183d3ce
--- /dev/null
+++ b/test/packetdrill/tests/linux/inet_diag/inet-diag-ipv6.pkt
@@ -0,0 +1,29 @@
+// Test inet_diag for AF_INET6 sockets with IPv6 traffic.
+// We use the "ss" socket statistics tool, which uses inet_diag sockets.
+// We use the default tcptest local IP address for IPv6.
+
+// Options (command line arguments in script file) to force IPv6.
+--ip_version=ipv6
+--mtu=1520
+
+// Establish a 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 <mss 1000,sackOK,nop,nop,nop,wscale 2>
++0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+
++0 `ss -6 -n state SYN-RECV | grep fd3d:fa7b:d17d::1:8080 > /dev/null`
+
++0 < . 1:1(0) ack 1 win 32890
+
++0 accept(3, ..., ...) = 4
+
+// first, use inet_diag with no filter:
++0 `ss -6 -n | grep :8080 | grep fd3d:fa7b:d17d::1:8080 > /dev/null`
+
+// then try filters, which use a different code path:
++0 `ss -6 -n --options --extended --info '( sport = :8080 )' | grep fd3d:fa7b:d17d::1:8080 > /dev/null`
++0 `ss -6 -n --options --extended --info '( sport = :8080 )' src fd3d:fa7b:d17d::1/128 | grep fd3d:fa7b:d17d::1:8080 > /dev/null`
diff --git a/test/packetdrill/tests/linux/init_rto/init_rto_passive_open.pkt b/test/packetdrill/tests/linux/init_rto/init_rto_passive_open.pkt
new file mode 100644
index 0000000..8775c3c
--- /dev/null
+++ b/test/packetdrill/tests/linux/init_rto/init_rto_passive_open.pkt
@@ -0,0 +1,17 @@
+// A simple test of initRTO (sysctl_tcp_synack_rto, default to 1sec) for
+// the passive open side.
+
+// We want the SYN-ACK to be retransmitted 1 sec after the SYN, but
+// usually it happens at 1.2 or 1.4 sec due to the fact that the
+// kernel only schedules SYN-ACK retransmissions periodically.
+// Specifically, the TCP_SYNQ_INTERVAL (period of the SYN-ACK timer) is 200ms.
+--tolerance_usecs=405000
+
+// Create a listener socket.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+1.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
diff --git a/test/packetdrill/tests/linux/initial_window/iw10-base-case.pkt b/test/packetdrill/tests/linux/initial_window/iw10-base-case.pkt
new file mode 100755
index 0000000..f790f56
--- /dev/null
+++ b/test/packetdrill/tests/linux/initial_window/iw10-base-case.pkt
@@ -0,0 +1,21 @@
+// A simple server-side test that sends exactly an initial window (IW10)
+// worth of packets.
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+0.200 write(4, ..., 14600) = 14600
+0.200 > P. 1:14601(14600) ack 1
+0.300 < . 1:1(0) ack 14601 win 257
+
+0.400 close(4) = 0
+0.401 > F. 14601:14601(0) ack 1
+0.501 < F. 1:1(0) ack 14602 win 257
+0.502 > . 14602:14602(0) ack 2
diff --git a/test/packetdrill/tests/linux/initial_window/iw10-short-response.pkt b/test/packetdrill/tests/linux/initial_window/iw10-short-response.pkt
new file mode 100755
index 0000000..6db3c4b
--- /dev/null
+++ b/test/packetdrill/tests/linux/initial_window/iw10-short-response.pkt
@@ -0,0 +1,21 @@
+// A simple server-side test that sends a response smaller
+// than the initial window of 10 MSS.
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+0.200 write(4, ..., 7300) = 7300
+0.200 > P. 1:7301(7300) ack 1
+0.300 < . 1:1(0) ack 7301 win 257
+
+0.400 close(4) = 0
+0.401 > F. 7301:7301(0) ack 1
+0.501 < F. 1:1(0) ack 7302 win 257
+0.502 > . 7302:7302(0) ack 2
diff --git a/test/packetdrill/tests/linux/ioctl/ioctl-siocinq-fin.pkt b/test/packetdrill/tests/linux/ioctl/ioctl-siocinq-fin.pkt
new file mode 100644
index 0000000..8499e02
--- /dev/null
+++ b/test/packetdrill/tests/linux/ioctl/ioctl-siocinq-fin.pkt
@@ -0,0 +1,30 @@
+// A simple test for the TCP SIOCINQ ioctl.
+
+// Create a socket.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+// Establish a connection.
+0.100 < S 0:0(0) win 20000 <mss 1000,sackOK,nop,nop>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK>
+0.200 < . 1:1(0) ack 1 win 20000
+0.200 accept(3, ..., ...) = 4
+
+// Receive a segment.
+0.200 < P. 1:1001(1000) ack 1 win 257
+0.200 > . 1:1(0) ack 1001
+
+0.210 ioctl(4, SIOCINQ, [1000]) = 0
+0.220 read(4, ..., 1000) = 1000
+0.230 ioctl(4, SIOCINQ, [0]) = 0
+
+// Receive a segment with a FIN.
+0.300 < FP. 1001:2001(1000) ack 1 win 257
+0.300 > . 1:1(0) ack 2002
+
+0.310 ioctl(4, SIOCINQ, [1000]) = 0
+0.320 read(4, ..., 1000) = 1000
+0.330 ioctl(4, SIOCINQ, [0]) = 0
diff --git a/test/packetdrill/tests/linux/listen/listen-incoming-ack.pkt b/test/packetdrill/tests/linux/listen/listen-incoming-ack.pkt
new file mode 100644
index 0000000..65f3733
--- /dev/null
+++ b/test/packetdrill/tests/linux/listen/listen-incoming-ack.pkt
@@ -0,0 +1,20 @@
+// Test behavior when a listener gets an incoming packet that has
+// the ACK bit set but not the SYN bit set.
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < . 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.100 > R 0:0(0) win 0
+
+// Now make sure that when a valid SYN arrives shortly thereafter
+// (with the same address 4-tuple) we can still successfully establish
+// a connection.
+
+0.200 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.200 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+
+0.300 < . 1:1(0) ack 1 win 320
+0.300 accept(3, ..., ...) = 4
diff --git a/test/packetdrill/tests/linux/listen/listen-incoming-no-tcp-flags.pkt b/test/packetdrill/tests/linux/listen/listen-incoming-no-tcp-flags.pkt
new file mode 100644
index 0000000..3ae1ff3
--- /dev/null
+++ b/test/packetdrill/tests/linux/listen/listen-incoming-no-tcp-flags.pkt
@@ -0,0 +1,21 @@
+// Test behavior when a listener gets an incoming packet that has
+// no TCP flags set.
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+// An incoming TCP segment with no TCP flags set.
+0.100 < - 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+// Linux ignores the packet and sends nothing.
+
+// Now make sure that when a valid SYN arrives shortly thereafter
+// (with the same address 4-tuple) we can still successfully establish
+// a connection.
+
+0.200 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.200 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+
+0.300 < . 1:1(0) ack 1 win 320
+0.300 accept(3, ..., ...) = 4
diff --git a/test/packetdrill/tests/linux/listen/listen-incoming-rst.pkt b/test/packetdrill/tests/linux/listen/listen-incoming-rst.pkt
new file mode 100644
index 0000000..83d25f2
--- /dev/null
+++ b/test/packetdrill/tests/linux/listen/listen-incoming-rst.pkt
@@ -0,0 +1,22 @@
+// Test behavior when a listener gets an incoming packet that has
+// the RST bit set but not the SYN bit set.
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < R 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+
+// The TCP stack should not respond to incoming RSTs, or else
+// we could get infinite RST ping-pong storms.
+
+// Now make sure that when a valid SYN arrives shortly thereafter
+// (with the same address 4-tuple) we can still successfully establish
+// a connection.
+
+0.200 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.200 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+
+0.300 < . 1:1(0) ack 1 win 320
+0.300 accept(3, ..., ...) = 4
diff --git a/test/packetdrill/tests/linux/listen/listen-incoming-syn-ack.pkt b/test/packetdrill/tests/linux/listen/listen-incoming-syn-ack.pkt
new file mode 100644
index 0000000..bc2569c
--- /dev/null
+++ b/test/packetdrill/tests/linux/listen/listen-incoming-syn-ack.pkt
@@ -0,0 +1,20 @@
+// Test behavior when a listener gets an incoming packet that has
+// the SYN and ACK bits set.
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S. 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.100 > R 0:0(0) win 0
+
+// Now make sure that when a valid SYN arrives shortly thereafter
+// (with the same address 4-tuple) we can still successfully establish
+// a connection.
+
+0.200 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.200 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+
+0.300 < . 1:1(0) ack 1 win 320
+0.300 accept(3, ..., ...) = 4
diff --git a/test/packetdrill/tests/linux/listen/listen-incoming-syn-rst.pkt b/test/packetdrill/tests/linux/listen/listen-incoming-syn-rst.pkt
new file mode 100644
index 0000000..f3c0607
--- /dev/null
+++ b/test/packetdrill/tests/linux/listen/listen-incoming-syn-rst.pkt
@@ -0,0 +1,22 @@
+// Test behavior when a listener gets an incoming packet that has
+// the SYN and RST bits set.
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < SR 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+
+// The TCP stack should not respond to incoming RSTs, or else
+// we could get infinite RST ping-pong storms.
+
+// Now make sure that when a valid SYN arrives shortly thereafter
+// (with the same address 4-tuple) we can still successfully establish
+// a connection.
+
+0.200 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.200 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+
+0.300 < . 1:1(0) ack 1 win 320
+0.300 accept(3, ..., ...) = 4
diff --git a/test/packetdrill/tests/linux/listen/listen-unbound.pkt b/test/packetdrill/tests/linux/listen/listen-unbound.pkt
new file mode 100644
index 0000000..fcf74fc
--- /dev/null
+++ b/test/packetdrill/tests/linux/listen/listen-unbound.pkt
@@ -0,0 +1,5 @@
+// Test behavior when a listener gets an incoming packet that has
+// the RST bit set but not the SYN bit set.
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 listen(3, 1) = 0 \ No newline at end of file
diff --git a/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-client-ts.pkt b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-client-ts.pkt
new file mode 100644
index 0000000..a8544f5
--- /dev/null
+++ b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-client-ts.pkt
@@ -0,0 +1,17 @@
+// Test that getsockopt of TCP_MAXSEG works on active/client TCP connections.
+// In this variant we test that a simple query of segment size works,
+// in the case where TCP timestamps reduce the usable payload space.
+
+// Create a socket.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+
+0.100...0.200 connect(3, ..., ...) = 0
+
+// Establish a connection.
+0.100 > S 0:0(0) <mss 1460,sackOK,TS val 100 ecr 0,nop,wscale 6>
+0.200 < S. 0:0(0) ack 1 win 32792 <mss 1100,sackOK,TS val 200 ecr 100,nop,wscale 7>
+0.200 > . 1:1(0) ack 1 <nop,nop,TS val 200 ecr 200>
+
+// Verify that the kernel reduced the returned segment size
+// to account for TCP timestamps.
+0.300 getsockopt(3, SOL_TCP, TCP_MAXSEG, [1088], [4]) = 0
diff --git a/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-client.pkt b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-client.pkt
new file mode 100644
index 0000000..a75b8b3
--- /dev/null
+++ b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-client.pkt
@@ -0,0 +1,14 @@
+// Test that getsockopt of TCP_MAXSEG works on active/client TCP connections.
+// In this variant we test that a simple query of segment size works.
+
+// Create a socket.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+
+0.100...0.200 connect(3, ..., ...) = 0
+
+// Establish a connection.
+0.100 > S 0:0(0) <mss 1460,sackOK,TS val 100 ecr 0,nop,wscale 6>
+0.200 < S. 0:0(0) ack 1 win 32792 <mss 1100,nop,wscale 7>
+0.200 > . 1:1(0) ack 1
+
+0.300 getsockopt(3, SOL_TCP, TCP_MAXSEG, [1100], [4]) = 0
diff --git a/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-advmss-ipv4.pkt b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-advmss-ipv4.pkt
new file mode 100644
index 0000000..c07c5c0
--- /dev/null
+++ b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-advmss-ipv4.pkt
@@ -0,0 +1,29 @@
+// Test that getsockopt of TCP_MAXSEG works on passive/server TCP connections.
+// In this variant we test that we get the expected result when
+// the routing config specifies an "advmss 1430 mtu lock 1470" for the
+// route to the remote IP under test.
+
+// To ensure that we do not cache something that interferes with other tests:
+--remote_ip="192.0.2.2"
+
+`ip route change 192.0.2.2 via 192.168.0.2 dev tun0 advmss 1430 mtu lock 1470`
+
+// Set up a listening socket.
+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
+
+// Establish a connection without timestamps.
++0 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
++0 > S. 0:0(0) ack 1 <mss 1430,nop,nop,sackOK,nop,wscale 6>
++0 < . 1:1(0) ack 1 win 257
+
++0 accept(3, ..., ...) = 4
+
+// Verify that the kernel returns the expected TCP max payload size.
++0 getsockopt(4, SOL_TCP, TCP_MAXSEG, [1430], [4]) = 0
+
++0 write(4, ..., 1500) = 1500
++0 > . 1:1431(1430) ack 1
++0 > P. 1431:1501(70) ack 1
diff --git a/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-advmss-ts-ipv4.pkt b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-advmss-ts-ipv4.pkt
new file mode 100644
index 0000000..2222d51
--- /dev/null
+++ b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-advmss-ts-ipv4.pkt
@@ -0,0 +1,30 @@
+// Test that getsockopt of TCP_MAXSEG works on passive/server TCP connections.
+// In this variant we test that we get the expected result when
+// the routing config specifies an "advmss 1430 mtu lock 1470" for the
+// route to the remote IP under test.
+
+// To ensure that we do not cache something that interferes with other tests:
+--remote_ip="192.0.2.2"
+
+`ip route change 192.0.2.2 via 192.168.0.2 dev tun0 advmss 1430 mtu lock 1470`
+
+// Set up a listening socket.
+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
+
+// Establish a connection
++0 < S 0:0(0) win 32792 <mss 1460,sackOK,TS val 0 ecr 0,nop,wscale 7>
++0 > S. 0:0(0) ack 1 <mss 1430,sackOK,TS val 0 ecr 0,nop,wscale 6>
++0 < . 1:1(0) ack 1 win 257 <nop,nop,TS val 0 ecr 0>
+
++0 accept(3, ..., ...) = 4
+
+// Verify that the kernel reduced the returned segment size
+// to account for TCP timestamps.
++0 getsockopt(4, SOL_TCP, TCP_MAXSEG, [1418], [4]) = 0
+
++0 write(4, ..., 1500) = 1500
++0 > . 1:1419(1418) ack 1 <nop,nop,TS val 0 ecr 0>
++0 > P. 1419:1501(82) ack 1 <nop,nop,TS val 0 ecr 0>
diff --git a/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-ts.pkt b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-ts.pkt
new file mode 100644
index 0000000..5d28c93
--- /dev/null
+++ b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server-ts.pkt
@@ -0,0 +1,20 @@
+// Test that getsockopt of TCP_MAXSEG works on passive/server TCP connections.
+// In this variant we test that a simple query of segment size works,
+// in the case where TCP timestamps reduce the usable payload space.
+
+// Set up a listening socket.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+// Establish a connection
+0.100 < S 0:0(0) win 32792 <mss 1100,sackOK,TS val 100 ecr 0,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,sackOK,TS val 100 ecr 100,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257 <nop,nop,TS val 200 ecr 100>
+
+0.300 accept(3, ..., ...) = 4
+
+// Verify that the kernel reduced the returned segment size
+// to account for TCP timestamps.
+0.400 getsockopt(4, SOL_TCP, TCP_MAXSEG, [1088], [4]) = 0
diff --git a/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server.pkt b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server.pkt
new file mode 100644
index 0000000..03516c1
--- /dev/null
+++ b/test/packetdrill/tests/linux/mss/mss-getsockopt-tcp_maxseg-server.pkt
@@ -0,0 +1,17 @@
+// Test that getsockopt of TCP_MAXSEG works on passive/server TCP connections.
+// In this variant we test that a simple query of segment size works.
+
+// Set up a listening socket.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+// Establish a connection
+0.100 < S 0:0(0) win 32792 <mss 1100,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+
+0.300 accept(3, ..., ...) = 4
+
+0.400 getsockopt(4, SOL_TCP, TCP_MAXSEG, [1100], [4]) = 0
diff --git a/test/packetdrill/tests/linux/mss/mss-setsockopt-tcp_maxseg-client.pkt b/test/packetdrill/tests/linux/mss/mss-setsockopt-tcp_maxseg-client.pkt
new file mode 100644
index 0000000..906ad6e
--- /dev/null
+++ b/test/packetdrill/tests/linux/mss/mss-setsockopt-tcp_maxseg-client.pkt
@@ -0,0 +1,24 @@
+// Test TCP_MAXSEG works on active/client TCP connections.
+
+// Create a socket.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+
+// Set MSS to 1100.
+0.010 setsockopt(3, SOL_TCP, TCP_MAXSEG, [1100], 4) = 0
+// TODO(ncardwell): the following is silly; should we fix it?
+0.020 getsockopt(3, SOL_TCP, TCP_MAXSEG, [536], [4]) = 0
+
+0.100...0.200 connect(3, ..., ...) = 0
+
+// Establish a connection with an outgoing advertised MSS of 1100.
+0.100 > S 0:0(0) <mss 1100,sackOK,TS val 100 ecr 0,nop,wscale 6>
+0.200 < S. 0:0(0) ack 1 win 32792 <mss 1460,nop,wscale 7>
+0.200 > . 1:1(0) ack 1
+
+0.300 getsockopt(3, SOL_TCP, TCP_MAXSEG, [1100], [4]) = 0
+
+0.400 %{ assert tcpi_advmss == 1100; assert tcpi_snd_mss == 1100 }%
+
+// IW10 MSS should yield outgoing TSO packet with 10*1100 == 11000 bytes:
+0.500 write(3, ..., 12000) = 12000
+0.500 > . 1:11001(11000) ack 1
diff --git a/test/packetdrill/tests/linux/mss/mss-setsockopt-tcp_maxseg-server.pkt b/test/packetdrill/tests/linux/mss/mss-setsockopt-tcp_maxseg-server.pkt
new file mode 100644
index 0000000..f2ed31d
--- /dev/null
+++ b/test/packetdrill/tests/linux/mss/mss-setsockopt-tcp_maxseg-server.pkt
@@ -0,0 +1,27 @@
+// Test TCP_MAXSEG works on passive/server TCP connections.
+
+// Set up a listening socket.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+// Set MSS to 1100.
+0.010 setsockopt(3, SOL_TCP, TCP_MAXSEG, [1100], 4) = 0
+// TODO(ncardwell): the following is silly; should we fix it?
+0.020 getsockopt(3, SOL_TCP, TCP_MAXSEG, [536], [4]) = 0
+
+// Establish a connection with an outgoing advertised MSS of 1100.
+0.100 < S 0:0(0) win 32792 <mss 1300,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1100,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+
+0.300 accept(3, ..., ...) = 4
+
+0.400 getsockopt(4, SOL_TCP, TCP_MAXSEG, [1100], [4]) = 0
+
+0.500 %{ assert tcpi_advmss == 1100; assert tcpi_snd_mss == 1100 }%
+
+// IW10 MSS should yield outgoing TSO packet with 10*1100 == 11000 bytes:
+0.600 write(4, ..., 12000) = 12000
+0.600 > . 1:11001(11000) ack 1
diff --git a/test/packetdrill/tests/linux/pmtu_discovery/pmtud-10pkt-1460-to-1160.pkt b/test/packetdrill/tests/linux/pmtu_discovery/pmtud-10pkt-1460-to-1160.pkt
new file mode 100644
index 0000000..df30dee
--- /dev/null
+++ b/test/packetdrill/tests/linux/pmtu_discovery/pmtud-10pkt-1460-to-1160.pkt
@@ -0,0 +1,54 @@
+// Test Path MTU discovery, RFC 1191.
+// This is a more substantive case, with 10*original_mss to send.
+// In this variant, we get an ICMP "unreachable frag_needed mtu 1200"
+// message and because the TCP sequence number is valid, TCP
+// immediately retransmits 'cwnd' packets using a smaller MSS
+// based on the MTU from the ICMP message.
+
+// Establish a connection.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+// Send 10 data segments.
+0.200 write(4, ..., 14600) = 14600
+0.200 > P. 1:14601(14600) ack 1
+
+// ICMP says that the first segment was too big.
+0.250 < [1:1461(1460)] icmp unreachable frag_needed mtu 1200
+// TCP picks a packet size using the MTU from the message, and
+// retransmits 'cwnd' packets:
+0.250 > . 1:1161(1160) ack 1
+0.250 > . 1161:2321(1160) ack 1
+0.250 > . 2321:3481(1160) ack 1
+0.250 > . 3481:4641(1160) ack 1
+0.250 > . 4641:5801(1160) ack 1
+0.250 > . 5801:6961(1160) ack 1
+0.250 > . 6961:8121(1160) ack 1
+0.250 > . 8121:9281(1160) ack 1
+0.250 > . 9281:10441(1160) ack 1
+0.250 > . 10441:11601(1160) ack 1
+
+// ACKs for packets retransmitted at a smaller MSS release yet more packets...
+
+0.350 < . 1:1(0) ack 1161 win 257
+0.350 > . 11601:12761(1160) ack 1
+0.350 > . 12761:13921(1160) ack 1
+
+0.355 < . 1:1(0) ack 2321 win 257
+0.355 > P. 13921:14601(680) ack 1
+
+// Receiver ACKs all data.
+0.455 < . 1:1(0) ack 14601 win 257
+
+// Clean up.
+0.500 close(4) = 0
+0.500 > F. 14601:14601(0) ack 1
+0.600 < F. 1:1(0) ack 14602 win 257
+0.600 > . 14602:14602(0) ack 2
diff --git a/test/packetdrill/tests/linux/pmtu_discovery/pmtud-1pkt-1460-to-1160.pkt b/test/packetdrill/tests/linux/pmtu_discovery/pmtud-1pkt-1460-to-1160.pkt
new file mode 100644
index 0000000..9776f10
--- /dev/null
+++ b/test/packetdrill/tests/linux/pmtu_discovery/pmtud-1pkt-1460-to-1160.pkt
@@ -0,0 +1,36 @@
+// Test Path MTU discovery, RFC 1191.
+// This is a simple case, with one packet to send.
+// In this variant, we get an ICMP "unreachable frag_needed mtu 1200"
+// message and because the TCP sequence number is valid, TCP
+// immediately retransmits our first outstanding packet
+// with a smaller MSS based on the MTU from the ICMP message.
+
+// Establish a connection.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1460,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+// Send 1 data segment.
+0.200 write(4, ..., 1460) = 1460
+0.200 > P. 1:1461(1460) ack 1
+
+// ICMP says that segment was too big.
+0.250 < [1:1461(1460)] icmp unreachable frag_needed mtu 1200
+// TCP picks a packet size using the MTU from the message, and retransmits.
+0.250 > . 1:1161(1160) ack 1
+0.250 > P. 1161:1461(300) ack 1
+
+// Receiver ACKs all data.
+0.350 < . 1:1(0) ack 1461 win 257
+
+// Clean up.
+1.300 close(4) = 0
+1.300 > F. 1461:1461(0) ack 1
+1.400 < F. 1:1(0) ack 1462 win 257
+1.400 > . 1462:1462(0) ack 2
diff --git a/test/packetdrill/tests/linux/receiver_rtt/rcv-rtt-with-timestamps-new.pkt b/test/packetdrill/tests/linux/receiver_rtt/rcv-rtt-with-timestamps-new.pkt
new file mode 100644
index 0000000..2155f21
--- /dev/null
+++ b/test/packetdrill/tests/linux/receiver_rtt/rcv-rtt-with-timestamps-new.pkt
@@ -0,0 +1,57 @@
+// Test that receiver-side RTT estimation is sane when
+// using TCP timestamps. We assert that the receive-side
+// RTT estimate is between 95 and 105ms.
+
+// Use a small receive buffer so that we advertise small windows, to keep the
+// test short.
+`sysctl -q net.ipv4.tcp_rmem="4096 10000 2097152"`
+
+// Create a socket.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+
+// Verify that the receive buffer is the tcp_rmem default.
+0.000 getsockopt(3, SOL_SOCKET, SO_RCVBUF, [10000], [4]) = 0
+
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+// Establish a connection.
+0.100 < S 0:0(0) win 20000 <mss 1000,sackOK,TS val 100 ecr 0>
+0.100 > S. 0:0(0) ack 1 <mss 1460,sackOK,TS val 100 ecr 100>
+0.200 < . 1:1(0) ack 1 win 20000 <nop,nop,TS val 200 ecr 100>
+0.200 accept(3, ..., ...) = 4
+0.200 %{ assert tcpi_rcv_rtt == 0 }%
+
+// First flight.
+0.200 < . 1:1001(1000) ack 1 win 20000 <nop,nop,TS val 200 ecr 100>
+0.200 > . 1:1(0) ack 1001 <nop,nop,TS val 200 ecr 200>
+0.200 < . 1001:2001(1000) ack 1 win 20000 <nop,nop,TS val 200 ecr 100>
+0.200 > . 1:1(0) ack 2001 <nop,nop,TS val 200 ecr 200>
+0.200 read(4, ..., 2000) = 2000
+0.200 %{ assert tcpi_rcv_rtt >= 95*1000 and tcpi_rcv_rtt <= 105*1000 }%
+
+// Second flight.
+0.300 < . 2001:3001(1000) ack 1 win 20000 <nop,nop,TS val 300 ecr 200>
+0.300 > . 1:1(0) ack 3001 <nop,nop,TS val 300 ecr 300>
+0.300 < . 3001:4001(1000) ack 1 win 20000 <nop,nop,TS val 300 ecr 200>
+0.300 > . 1:1(0) ack 4001 <nop,nop,TS val 300 ecr 300>
+0.300 < . 4001:5001(1000) ack 1 win 20000 <nop,nop,TS val 300 ecr 200>
+0.300 > . 1:1(0) ack 5001 <nop,nop,TS val 300 ecr 300>
+0.300 < . 5001:6001(1000) ack 1 win 20000 <nop,nop,TS val 300 ecr 200>
+0.300 read(4, ..., 4000) = 4000
+0.300 > . 1:1(0) ack 6001 <nop,nop,TS val 300 ecr 300>
+0.300 %{ assert tcpi_rcv_rtt >= 95*1000 and tcpi_rcv_rtt <= 105*1000 }%
+
+// Third flight.
+// We omit outgoing ACKs because we don't care about this behavior,
+// and don't want to introduce dependencies on the receive window behavior.
+0.400 < . 6001:7001(1000) ack 1 win 20000 <nop,nop,TS val 400 ecr 300>
+0.400 < . 7001:8001(1000) ack 1 win 20000 <nop,nop,TS val 400 ecr 300>
+0.400 < . 8001:9001(1000) ack 1 win 20000 <nop,nop,TS val 400 ecr 300>
+0.400 < . 9001:10001(1000) ack 1 win 20000 <nop,nop,TS val 400 ecr 300>
+0.400 < . 10001:11001(1000) ack 1 win 20000 <nop,nop,TS val 400 ecr 300>
+0.400 read(4, ..., 5000) = 5000
+0.400 %{ assert tcpi_rcv_rtt >= 95*1000 and tcpi_rcv_rtt <= 105*1000 }%
+
+0.500 `sysctl -q net.ipv4.tcp_rmem="4096 87380 3732736"`
diff --git a/test/packetdrill/tests/linux/receiver_rtt/rcv-rtt-without-timestamps-new.pkt b/test/packetdrill/tests/linux/receiver_rtt/rcv-rtt-without-timestamps-new.pkt
new file mode 100644
index 0000000..e963993
--- /dev/null
+++ b/test/packetdrill/tests/linux/receiver_rtt/rcv-rtt-without-timestamps-new.pkt
@@ -0,0 +1,62 @@
+// Test that receiver-side RTT estimation is sane when
+// *not* using TCP timestamps. When we are not using timestamps
+// then the receive-side RTT estimation logic uses as an RTT
+// sample the time elapsed between (a) when the receiver advertises
+// that a sender may send sequence number N, and (b) when the
+// sequence number N arrives. In this (not unusual) case below,
+// this takes 1 RTT; so we assert that the receive-side
+// RTT estimate is between 95 and 105ms.
+
+// Use a small receive buffer so that we advertise small windows, to keep the
+// test short.
+`sysctl -q net.ipv4.tcp_rmem="4096 10000 2097152"`
+
+// Create a socket.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+
+// Verify that the receive buffer is the tcp_rmem default.
+0.000 getsockopt(3, SOL_SOCKET, SO_RCVBUF, [10000], [4]) = 0
+
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+// Establish a connection.
+0.100 < S 0:0(0) win 20000 <mss 1000,nop,nop,sackOK>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK>
+0.200 < . 1:1(0) ack 1 win 20000
+0.200 accept(3, ..., ...) = 4
+0.200 %{ assert tcpi_rcv_rtt == 0 }%
+
+// First flight.
+0.200 < . 1:1001(1000) ack 1 win 20000
+0.200 > . 1:1(0) ack 1001
+0.200 < . 1001:2001(1000) ack 1 win 20000
+0.200 > . 1:1(0) ack 2001
+0.200 read(4, ..., 2000) = 2000
+0.200 %{ assert tcpi_rcv_rtt == 0 }%
+
+// Second flight.
+0.300 < . 2001:3001(1000) ack 1 win 20000
+0.300 > . 1:1(0) ack 3001
+0.300 < . 3001:4001(1000) ack 1 win 20000
+0.300 > . 1:1(0) ack 4001
+0.300 < . 4001:5001(1000) ack 1 win 20000
+0.300 > . 1:1(0) ack 5001
+0.300 < . 5001:6001(1000) ack 1 win 20000
+0.300 read(4, ..., 4000) = 4000
+0.300 > . 1:1(0) ack 6001
+0.300 %{ assert tcpi_rcv_rtt >= 95*1000 and tcpi_rcv_rtt <= 105*1000 }%
+
+// Third flight.
+// We omit outgoing ACKs because we don't care about this behavior,
+// and don't want to introduce dependencies on the receive window behavior.
+0.400 < . 6001:7001(1000) ack 1 win 20000
+0.400 < . 7001:8001(1000) ack 1 win 20000
+0.400 < . 8001:9001(1000) ack 1 win 20000
+0.400 < . 9001:10001(1000) ack 1 win 20000
+0.400 < . 10001:11001(1000) ack 1 win 20000
+0.400 read(4, ..., 5000) = 5000
+0.400 %{ assert tcpi_rcv_rtt >= 95*1000 and tcpi_rcv_rtt <= 105*1000 }%
+
+0.500 `sysctl -q net.ipv4.tcp_rmem="4096 87380 3732736"`
diff --git a/test/packetdrill/tests/linux/run_tests.sh b/test/packetdrill/tests/linux/run_tests.sh
new file mode 100755
index 0000000..309cd20
--- /dev/null
+++ b/test/packetdrill/tests/linux/run_tests.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+for f in `find . -name "*.pkt" | sort`; do
+ echo "Running $f ..."
+ ip tcp_metrics flush all > /dev/null 2>&1
+ ../../packetdrill $f
+done
diff --git a/test/packetdrill/tests/linux/sack/sack-shift-sacked-1-2-3-fack.pkt b/test/packetdrill/tests/linux/sack/sack-shift-sacked-1-2-3-fack.pkt
new file mode 100644
index 0000000..52b8cda
--- /dev/null
+++ b/test/packetdrill/tests/linux/sack/sack-shift-sacked-1-2-3-fack.pkt
@@ -0,0 +1,47 @@
+// Test shifting of newly-SACKed ranges onto the previous already-SACKed skb.
+// This variant SACKs segments 1, 2, and 3.
+
+// Establish a connection and send 10 MSS.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 1024
+0.200 accept(3, ..., ...) = 4
+
+0.200 write(4, ..., 10000) = 10000
+0.200 > P. 1:10001(10000) ack 1
+0.200 %{
+assert tcpi_reordering == 3
+assert tcpi_unacked == 10
+assert tcpi_sacked == 0
+}%
+
+0.300 < . 1:1(0) ack 1 win 257 <sack 1001:2001,nop,nop>
+0.300 %{
+assert tcpi_reordering == 3
+assert tcpi_unacked == 10
+assert tcpi_sacked == 1
+}%
+
+// This SACK for an adjacent range causes the sender to
+// shift the newly-SACKed range onto the previous skb.
+0.310 < . 1:1(0) ack 1 win 257 <sack 1001:3001,nop,nop>
+0.310 %{
+assert tcpi_reordering == 3
+assert tcpi_unacked == 10
+assert tcpi_sacked == 2
+}%
+
+// This SACK for an adjacent range causes the sender to
+// shift the newly-SACKed range onto the previous skb.
+0.320 < . 1:1(0) ack 1 win 257 <sack 1001:4001,nop,nop>
+0.320 %{
+assert tcpi_reordering == 3
+assert tcpi_unacked == 10
+assert tcpi_sacked == 3
+assert tcpi_ca_state == TCP_CA_Recovery
+}%
diff --git a/test/packetdrill/tests/linux/sack/sack-shift-sacked-1-2:6-fack.pkt b/test/packetdrill/tests/linux/sack/sack-shift-sacked-1-2:6-fack.pkt
new file mode 100644
index 0000000..464ba69
--- /dev/null
+++ b/test/packetdrill/tests/linux/sack/sack-shift-sacked-1-2:6-fack.pkt
@@ -0,0 +1,39 @@
+// Test shifting of newly-SACKed ranges onto the previous already-SACKed skb.
+// This variant receives a SACK for segment 1 and then a SACK for
+// segments 1-6, to check handling of large newly-SACKed ranges.
+
+// Establish a connection and send 10 MSS.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 1024
+0.200 accept(3, ..., ...) = 4
+
+0.200 write(4, ..., 10000) = 10000
+0.200 > P. 1:10001(10000) ack 1
+0.200 %{
+assert tcpi_reordering == 3
+assert tcpi_unacked == 10
+assert tcpi_sacked == 0
+}%
+
+0.300 < . 1:1(0) ack 1 win 257 <sack 1001:2001,nop,nop>
+0.300 %{
+assert tcpi_reordering == 3
+assert tcpi_unacked == 10
+assert tcpi_sacked == 1
+}%
+
+// This SACK for an adjacent range causes the sender to
+// shift the newly-SACKed range onto the previous skb.
+0.310 < . 1:1(0) ack 1 win 257 <sack 1001:7001,nop,nop>
+0.310 %{
+assert tcpi_reordering == 3
+assert tcpi_unacked == 10
+assert tcpi_sacked == 6
+assert tcpi_ca_state == TCP_CA_Recovery
+}%
diff --git a/test/packetdrill/tests/linux/shutdown/shutdown-rd-close.pkt b/test/packetdrill/tests/linux/shutdown/shutdown-rd-close.pkt
new file mode 100644
index 0000000..f5fff60
--- /dev/null
+++ b/test/packetdrill/tests/linux/shutdown/shutdown-rd-close.pkt
@@ -0,0 +1,29 @@
+// Verify behavior for the sequence:
+// shutdown(SHUT_RD), close().
+
+// Initialize a server socket.
+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 <mss 1000,sackOK,nop,nop,nop,wscale 7>
++0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
++0 < . 1:1(0) ack 1 win 257
+
++0 accept(3, ..., ...) = 4
+
++.010 shutdown(4, SHUT_RD) = 0
++0 read(4, ..., 1000) = 0
+
+// Verify that writing and sending still works.
++.010 write(4, ..., 1000) = 1000
++0 > P. 1:1001(1000) ack 1
++0 < . 1:1(0) ack 1001 win 257
+
++.010 close(4) = 0
++0 > F. 1001:1001(0) ack 1
++0 < . 1:1(0) ack 1002 win 257
+
++.010 < F. 1:1(0) ack 1002 win 257
++0 > . 1002:1002(0) ack 2
diff --git a/test/packetdrill/tests/linux/shutdown/shutdown-rd-wr-close.pkt b/test/packetdrill/tests/linux/shutdown/shutdown-rd-wr-close.pkt
new file mode 100644
index 0000000..5b97fad
--- /dev/null
+++ b/test/packetdrill/tests/linux/shutdown/shutdown-rd-wr-close.pkt
@@ -0,0 +1,45 @@
+// Verify behavior for the sequence:
+// shutdown(SHUT_RD), receive, send, shutdown(SHUT_WR), close().
+
+// Initialize a server socket.
+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 <mss 1000,sackOK,nop,nop,nop,wscale 7>
++0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
++0 < . 1:1(0) ack 1 win 257
+
++0 accept(3, ..., ...) = 4
+
++.010 shutdown(4, SHUT_RD) = 0
+
++0 read(4, ..., 1000) = 0
+
+// You would think that after SHUT_RD we would respond to incoming
+// data with a RST and not queue the data for reading, but we actually
+// ACK the data, enqueue it for reading, and can read() the data.
+// AFAICT in 2003 Andi Kleen seems to have decided that this case is too
+// obscure to slow down the fast path for receiving and reading data:
+// http://marc.info/?l=linux-netdev&m=105774722214242&w=2
+// So....
+// Verify that receiving and reading still works.
++0 < . 1:1001(1000) ack 1 win 257
++0 > . 1:1(0) ack 1001
++0 read(4, ..., 1000) = 1000
+
+// Verify that writing and sending still works.
++.010 write(4, ..., 1000) = 1000
++0 > P. 1:1001(1000) ack 1001
++0 < . 1001:1001(0) ack 1001 win 257
+
++.010 shutdown(4, SHUT_WR) = 0
++0 > F. 1001:1001(0) ack 1001
++0 < . 1001:1001(0) ack 1002 win 257
++0 write(4, ..., 1000) = -1 EPIPE (Broken pipe)
+
++.010 close(4) = 0
+
++.010 < F. 1001:1001(0) ack 1002 win 257
++0 > . 1002:1002(0) ack 1002
diff --git a/test/packetdrill/tests/linux/shutdown/shutdown-rdwr-close.pkt b/test/packetdrill/tests/linux/shutdown/shutdown-rdwr-close.pkt
new file mode 100644
index 0000000..cb55c3b
--- /dev/null
+++ b/test/packetdrill/tests/linux/shutdown/shutdown-rdwr-close.pkt
@@ -0,0 +1,26 @@
+// Verify behavior for the sequence:
+// shutdown(SHUT_RDWR), close().
+
+// Initialize a server socket.
+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 <mss 1000,sackOK,nop,nop,nop,wscale 7>
++0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
++0 < . 1:1(0) ack 1 win 257
+
++0 accept(3, ..., ...) = 4
+
++.010 shutdown(4, SHUT_RDWR) = 0
++0 > F. 1:1(0) ack 1
++0 < . 1:1(0) ack 2 win 257
+
++0 read(4, ..., 1000) = 0
++0 write(4, ..., 1000) = -1 EPIPE (Broken pipe)
+
++.010 close(4) = 0
+
++.010 < F. 1:1(0) ack 2 win 257
++0 > . 2:2(0) ack 2
diff --git a/test/packetdrill/tests/linux/shutdown/shutdown-wr-close.pkt b/test/packetdrill/tests/linux/shutdown/shutdown-wr-close.pkt
new file mode 100644
index 0000000..c840f84
--- /dev/null
+++ b/test/packetdrill/tests/linux/shutdown/shutdown-wr-close.pkt
@@ -0,0 +1,29 @@
+// Verify behavior for the sequence:
+// shutdown(SHUT_WR), close().
+
+// Initialize a server socket.
+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 <mss 1000,sackOK,nop,nop,nop,wscale 7>
++0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
++0 < . 1:1(0) ack 1 win 257
+
++0 accept(3, ..., ...) = 4
+
++.010 shutdown(4, SHUT_WR) = 0
++0 > F. 1:1(0) ack 1
++0 < . 1:1(0) ack 2 win 257
++0 write(4, ..., 1000) = -1 EPIPE (Broken pipe)
+
+// Verify that receiving and reading still works.
++.010 < . 1:1001(1000) ack 2 win 257
++0 > . 2:2(0) ack 1001
++0 read(4, ..., 1000) = 1000
+
++.010 close(4) = 0
+
++.010 < F. 1001:1001(0) ack 2 win 257
++0 > . 2:2(0) ack 1002
diff --git a/test/packetdrill/tests/linux/undo/undo-fr-ack-then-dsack-on-ack-below-snd_una.pkt b/test/packetdrill/tests/linux/undo/undo-fr-ack-then-dsack-on-ack-below-snd_una.pkt
new file mode 100644
index 0000000..b3347f9
--- /dev/null
+++ b/test/packetdrill/tests/linux/undo/undo-fr-ack-then-dsack-on-ack-below-snd_una.pkt
@@ -0,0 +1,55 @@
+// Test fast recovery and undo: send 10 MSS, get 3 dupacks, do a
+// fast retransmit, get a DSACK for the retransmitted segment, and
+// undo the cwnd reduction.
+// Assumes initial cwnd is 10. Receiver supports SACK.
+//
+// In this variant there is reordering in the return path,
+// so that we end up getting an ACK below snd_una that
+// has the critical DSACK that tells us we need to undo.
+
+// Establish a connection.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+// Send 10 MSS.
+0.200 write(4, ..., 10000) = 10000
+0.200 > P. 1:10001(10000) ack 1
+
+// Get 3 dupacks.
+0.300 < . 1:1(0) ack 1 win 257 <sack 1001:2001,nop,nop>
+0.300 < . 1:1(0) ack 1 win 257 <sack 1001:3001,nop,nop>
+0.300 < . 1:1(0) ack 1 win 257 <sack 1001:4001,nop,nop>
+// We've received 3 duplicate ACKs, so we do a fast retransmit.
+0.300 > . 1:1001(1000) ack 1
+// Apparently just reordering; receiver ACKs all data. Retransmit was spurious.
+0.300 < . 1:1(0) ack 4001 win 257
+0.300 < . 1:1(0) ack 6001 win 257
+0.300 < . 1:1(0) ack 8001 win 257
+0.300 < . 1:1(0) ack 10001 win 257
+
+// We send some more new data so we can have an ACK that races our DSACK.
+0.303 write(4, ..., 1000) = 1000
+0.303 > P. 10001:11001(1000) ack 1
+
+// Receiver ACKs all outstanding data.
+0.400 < . 1:1(0) ack 11001 win 257
+
+// Oops; there was reordering in the ACK path!
+// Now we get the DSACK for the retransmitted packet.
+// It's a DSACK on an ack below snd_una.
+0.401 < . 1:1(0) ack 10001 win 257 <sack 1:1001,nop,nop>
+
+// Verify that the DSACK caused an undo, restoring cwnd to 10.
+0.450 write(4, ..., 11000) = 11000
+0.450 > . 11001:21001(10000) ack 1
+0.450 %{
+assert tcpi_snd_cwnd == 10
+assert tcpi_unacked == 10
+}%
diff --git a/test/packetdrill/tests/linux/undo/undo-fr-acks-dropped-then-dsack.pkt b/test/packetdrill/tests/linux/undo/undo-fr-acks-dropped-then-dsack.pkt
new file mode 100644
index 0000000..f39ddc8
--- /dev/null
+++ b/test/packetdrill/tests/linux/undo/undo-fr-acks-dropped-then-dsack.pkt
@@ -0,0 +1,44 @@
+// Test fast recovery and undo: send 10 MSS, get 3 dupacks, do a
+// fast retransmit, get a DSACK for the retransmitted segment, and
+// undo the cwnd reduction.
+// Assumes initial cwnd is 10. Receiver supports SACK.
+//
+// In this variant the original ACKs are lost, and all the sender gets is
+// a DSACK.
+
+// Establish a connection.
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+
+0.100 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0.100 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 6>
+0.200 < . 1:1(0) ack 1 win 257
+0.200 accept(3, ..., ...) = 4
+
+// Send 10 MSS.
+0.200 write(4, ..., 10000) = 10000
+0.200 > P. 1:10001(10000) ack 1
+
+// Get 3 dupacks.
+0.300 < . 1:1(0) ack 1 win 257 <sack 1001:2001,nop,nop>
+0.300 < . 1:1(0) ack 1 win 257 <sack 1001:3001,nop,nop>
+0.300 < . 1:1(0) ack 1 win 257 <sack 1001:4001,nop,nop>
+// We've received 3 duplicate ACKs, so we do a fast retransmit.
+0.300 > . 1:1001(1000) ack 1
+0.300 %{ assert tcpi_snd_cwnd == 7 }%
+
+// Apparently just reordering. Retransmit was spurious.
+// Original ACKs for sequence ranges up to 10001 are all lost.
+
+// Receiver sends DSACK for retransmitted packet.
+0.400 < . 1:1(0) ack 10001 win 257 <sack 1:1001,nop,nop>
+
+// Verify that the DSACK caused an undo, restoring cwnd to 10.
+0.400 write(4, ..., 11000) = 11000
+0.400 > . 10001:20001(10000) ack 1
+0.400 %{
+assert tcpi_snd_cwnd == 10
+assert tcpi_unacked == 10
+}%