Merge branch 'selftest-packetdrill-import-tfo-server-tests'

Kuniyuki Iwashima says:

====================
selftest: packetdrill: Import TFO server tests.

The series imports 15 TFO server tests from google/packetdrill and
adds 2 more tests.

The repository has two versions of tests for most scenarios; one uses
the non-experimental option (34), and the other uses the experimental
option (255) with 0xF989.

Basically, we only import the non-experimental version of tests, and
for the experimental option, tcp_fastopen_server_experimental_option.pkt
is added.

The following tests are not (yet) imported:

  * icmp-baseline.pkt
  * simple1.pkt / simple2.pkt / simple3.pkt

The former is completely covered by icmp-before-accept.pkt.

The later's delta is the src/dst IP pair to generate a different
cookie, but supporting dualstack requires churn in ksft_runner.sh,
so defered to future series.  Also, sockopt-fastopen-key.pkt covers
the same function.

The following tests have the experimental version only, so converted
to the non-experimental option:

  * client-ack-dropped-then-recovery-ms-timestamps.pkt
  * sockopt-fastopen-key.pkt

For the imported tests, these common changes are applied.

  * Add SPDX header
  * Adjust path to default.sh
  * Adjust sysctl w/ set_sysctls.py
  * Use TFO_COOKIE instead of a raw hex value
  * Use SOCK_NONBLOCK for socket() not to block accept()
  * Add assertions for TCP state if commented
  * Remove unnecessary delay (e.g. +0.1 setsockopt(SO_REUSEADDR), etc)

With this series, except for simple{1,2,3}.pkt, we can remove TFO server
tests in google/packetdrill.
====================

Link: https://patch.msgid.link/20250927213022.1850048-1-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-09-29 18:41:39 -07:00
commit 4363d18219
20 changed files with 611 additions and 6 deletions

View File

@ -51,7 +51,8 @@ sysctl -q net.ipv4.tcp_pacing_ss_ratio=200
sysctl -q net.ipv4.tcp_pacing_ca_ratio=120
sysctl -q net.ipv4.tcp_notsent_lowat=4294967295 > /dev/null 2>&1
sysctl -q net.ipv4.tcp_fastopen=0x70403
sysctl -q net.ipv4.tcp_fastopen=0x3
# Use TFO_COOKIE in ksft_runner.sh for this key.
sysctl -q net.ipv4.tcp_fastopen_key=a1a1a1a1-b2b2b2b2-c3c3c3c3-d4d4d4d4
sysctl -q net.ipv4.tcp_syncookies=1

View File

@ -9,6 +9,8 @@ declare -A ip_args=(
--gateway_ip=192.168.0.1
--netmask_ip=255.255.0.0
--remote_ip=192.0.2.1
-D TFO_COOKIE=3021b9d889017eeb
-D TFO_COOKIE_ZERO=b7c12350a90dc8f5
-D CMSG_LEVEL_IP=SOL_IP
-D CMSG_TYPE_RECVERR=IP_RECVERR"
[ipv6]="--ip_version=ipv6
@ -16,6 +18,8 @@ declare -A ip_args=(
--local_ip=fd3d:0a0b:17d6::1
--gateway_ip=fd3d:0a0b:17d6:8888::1
--remote_ip=fd3d:fa7b:d17d::1
-D TFO_COOKIE=c1d1e9742a47a9bc
-D TFO_COOKIE_ZERO=82af1a8f9a205c34
-D CMSG_LEVEL_IP=SOL_IPV6
-D CMSG_TYPE_RECVERR=IPV6_RECVERR"
)
@ -48,11 +52,11 @@ elif [[ ! "$ip_versions" =~ ^ipv[46]$ ]]; then
fi
ktap_print_header
ktap_set_plan 2
ktap_set_plan $(echo $ip_versions | wc -w)
for ip_version in $ip_versions; do
unshare -n packetdrill ${ip_args[$ip_version]} ${optargs[@]} $script > /dev/null \
&& ktap_test_pass $ip_version || $failfunc $ip_version
&& ktap_test_pass $ip_version || $failfunc $ip_version
done
ktap_finished

View File

@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0
//
// Basic TFO server test
//
// Test TFO_SERVER_COOKIE_NOT_REQD flag on receiving
// SYN with data but without Fast Open cookie option.
`./defaults.sh
./set_sysctls.py /proc/sys/net/ipv4/tcp_fastopen=0x202`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
// Since TFO_SERVER_COOKIE_NOT_REQD, a TFO socket will be created with
// the data accepted.
+0 < S 0:1000(1000) win 32792 <mss 1460,sackOK,nop,nop>
+0 > S. 0:0(0) ack 1001 <mss 1460,nop,nop,sackOK>
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }%
+0 read(4, ..., 1024) = 1000
// Data After SYN will be accepted too.
+0 < . 1001:2001(1000) ack 1 win 5840
+0 > . 1:1(0) ack 2001
// Should change the implementation later to set the SYN flag as well.
+0 read(4, ..., 1024) = 1000
+0 write(4, ..., 1000) = 1000
+0 > P. 1:1001(1000) ack 2001

View File

@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
//
// Basic TFO server test
//
// Test TFO_SERVER_WO_SOCKOPT1 without setsockopt(TCP_FASTOPEN)
`./defaults.sh
./set_sysctls.py /proc/sys/net/ipv4/tcp_fastopen=0x402`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, 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:10(10) win 32792 <mss 1460,sackOK,nop,nop,FO TFO_COOKIE,nop,nop>
+0 > S. 0:0(0) ack 11 <mss 1460,nop,nop,sackOK>
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }%
+0 read(4, ..., 512) = 10

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
//
// Basic TFO server test
//
// Server w/o TCP_FASTOPEN socket option
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, 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:10(10) win 32792 <mss 1460,sackOK,FO TFO_COOKIE>
// Data is ignored since TCP_FASTOPEN is not set on the listener
+0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK>
+0 accept(3, ..., ...) = -1 EAGAIN (Resource temporarily unavailable)
// The above should block until ack comes in below.
+0 < . 1:31(30) ack 1 win 5840
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }%
+0 read(4, ..., 512) = 30

View File

@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0
//
// Basic TFO server test
//
// Test that TFO-enabled server would not respond SYN-ACK with any TFO option
// when receiving a pure SYN-data. It should respond a pure SYN-ack.
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 999000:999040(40) win 32792 <mss 1460,sackOK,TS val 100 ecr 100,nop,wscale 6>
+0 > S. 1234:1234(0) ack 999001 <mss 1460,sackOK,TS val 100 ecr 100,nop,wscale 8>
+0 < . 1:1(0) ack 1 win 100
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }%
+0 close(3) = 0
// Test ECN-setup SYN with ECN disabled because this has happened in reality
+0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < SEW 999000:999040(40) win 32792 <mss 1460,sackOK,TS val 100 ecr 100,nop,wscale 6>
+0 > S. 1234:1234(0) ack 999001 <mss 1460,sackOK,TS val 100 ecr 100,nop,wscale 8>
+0 < . 1:1(0) ack 1 win 100
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }%
+0 close(3) = 0
// Test ECN-setup SYN w/ ECN enabled
+0 `sysctl -q net.ipv4.tcp_ecn=2`
+0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < SEW 999000:999040(40) win 32792 <mss 1460,sackOK,TS val 100 ecr 100,nop,wscale 6>
+0 > SE. 1234:1234(0) ack 999001 <mss 1460,sackOK,TS val 100 ecr 100,nop,wscale 8>
+0 < . 1:1(0) ack 1 win 100
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }%
+0 close(3) = 0

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0
//
// Basic TFO server test
//
// Test TFO server with SYN that has TFO cookie and data.
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:10(10) win 32792 <mss 1460,sackOK,nop,nop,FO TFO_COOKIE,nop,nop>
+0 > S. 0:0(0) ack 11 <mss 1460,nop,nop,sackOK>
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }%
+0 read(4, ..., 512) = 10
+0 write(4, ..., 100) = 100
+0 > P. 1:101(100) ack 11

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
//
// Basic TFO server test
//
// Test zero-payload packet w/ valid TFO cookie - a TFO socket will
// still be created and accepted but read() will not return until a
// later pkt with 10 byte.
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,FO TFO_COOKIE,nop,nop>
+0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK>
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }%
// A TFO socket is created and is writable.
+0 write(4, ..., 100) = 100
+0 > P. 1:101(100) ack 1
+0...0.300 read(4, ..., 512) = 10
+.3 < P. 1:11(10) ack 1 win 5840

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0
//
// A reproducer case for a TFO SYNACK RTO undo bug in:
// 794200d66273 ("tcp: undo cwnd on Fast Open spurious SYNACK retransmit")
// This sequence that tickles this bug is:
// - Fast Open server receives TFO SYN with data, sends SYNACK
// - (client receives SYNACK and sends ACK, but ACK is lost)
// - server app sends some data packets
// - (N of the first data packets are lost)
// - server receives client ACK that has a TS ECR matching first SYNACK,
// and also SACKs suggesting the first N data packets were lost
// - server performs undo of SYNACK RTO, then immediately enters recovery
// - buggy behavior in 794200d66273 then performed an undo that caused
// the connection to be in a bad state, in CA_Open with retrans_out != 0
// Check that outbound TS Val ticks are as we would expect with 1000 usec per
// timestamp tick:
--tcp_ts_tick_usecs=1000
`./defaults.sh`
// Initialize connection
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 < S 0:1000(1000) win 65535 <mss 1012,sackOK,TS val 1000 ecr 0,wscale 7,nop,nop,nop,FO TFO_COOKIE>
+0 > S. 0:0(0) ack 1001 <mss 1460,sackOK,TS val 2000 ecr 1000,nop,wscale 8>
+0 accept(3, ..., ...) = 4
// Application writes more data
+.010 write(4, ..., 10000) = 10000
+0 > P. 1:5001(5000) ack 1001 <nop,nop,TS val 2010 ecr 1000>
+0 > P. 5001:10001(5000) ack 1001 <nop,nop,TS val 2010 ecr 1000>
+0 %{ assert tcpi_snd_cwnd == 10, tcpi_snd_cwnd }%
+0 < . 1001:1001(0) ack 1 win 257 <TS val 1010 ecr 2000,sack 2001:5001>
+0 > P. 1:2001(2000) ack 1001 <nop,nop,TS val 2010 ecr 1010>
+0 %{ assert tcpi_ca_state == TCP_CA_Recovery, tcpi_ca_state }%
+0 %{ assert tcpi_snd_cwnd == 7, tcpi_snd_cwnd }%
+0 < . 1001:1001(0) ack 1 win 257 <TS val 1011 ecr 2000,sack 2001:6001>
+0 %{ assert tcpi_ca_state == TCP_CA_Recovery, tcpi_ca_state }%
+0 %{ assert tcpi_snd_cwnd == 7, tcpi_snd_cwnd }%

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0
//
// Test the Experimental Option
//
// SYN w/ FOEXP w/o cookie must generates SYN+ACK w/ FOEXP
// w/ a valid cookie, and the cookie must be the same one
// with one generated by IANA FO
`./defaults.sh`
// Request a TFO cookie by Experimental Option
// This must generate the same TFO_COOKIE
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:10(10) win 32792 <mss 1460,sackOK,nop,nop,FOEXP>
+0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,FOEXP TFO_COOKIE>
+0 close(3) = 0
// Test if FOEXP with a valid cookie creates a TFO socket
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:10(10) win 32792 <mss 1460,sackOK,nop,nop,FOEXP TFO_COOKIE>
+0 > S. 0:0(0) ack 11 <mss 1460,nop,nop,sackOK>
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }%
+0 read(4, ..., 512) = 10

View File

@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-2.0
//
// Send a FIN pkt with the ACK bit to a TFO socket.
// The socket will go to TCP_CLOSE_WAIT state and data can be
// read until the socket is closed, at which time a FIN will be sent.
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:10(10) win 32792 <mss 1460,sackOK,nop,nop,FO TFO_COOKIE,nop,nop>
+0 > S. 0:0(0) ack 11 <mss 1460,nop,nop,sackOK>
// FIN is acked and the socket goes to TCP_CLOSE_WAIT state
// in tcp_fin() called from tcp_data_queue().
+0 < F. 11:11(0) ack 1 win 32792
+0 > . 1:1(0) ack 12
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }%
+0 %{ assert tcpi_state == TCP_CLOSE_WAIT, tcpi_state }%
+0 read(4, ..., 512) = 10
+0 close(4) = 0
+0 > F. 1:1(0) ack 12
* > F. 1:1(0) ack 12

View File

@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0
//
// Send an ICMP host_unreachable pkt to a pending SYN_RECV req.
//
// If it's a TFO req, the ICMP error will cause it to switch
// to TCP_CLOSE state but remains in the acceptor queue.
--ip_version=ipv4
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:10(10) win 32792 <mss 1460,sackOK,nop,nop,FO TFO_COOKIE,nop,nop>
+0 > S. 0:0(0) ack 11 <mss 1460,nop,nop,sackOK>
// Out-of-window icmp is ignored but accounted.
+0 `nstat > /dev/null`
+0 < icmp unreachable [5000:6000(1000)]
+0 `nstat | grep TcpExtOutOfWindowIcmps > /dev/null`
// Valid ICMP unreach.
+0 < icmp unreachable host_unreachable [0:10(10)]
// Unlike the non-TFO case, the req is still there to be accepted.
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }%
// tcp_done_with_error() in tcp_v4_err() sets sk->sk_state
// to TCP_CLOSE
+0 %{ assert tcpi_state == TCP_CLOSE, tcpi_state }%
// The 1st read will succeed and return the data in SYN
+0 read(4, ..., 512) = 10
// The 2nd read will fail.
+0 read(4, ..., 512) = -1 EHOSTUNREACH (No route to host)
// But is no longer writable because it's in TCP_CLOSE state.
+0 write(4, ..., 100) = -1 EPIPE (Broken Pipe)
// inbound pkt will trigger RST because the socket has been moved
// off the TCP hash tables.
+0 < . 1:1(0) ack 1 win 32792
+0 > R 1:1(0)

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0
//
// Send a RST to a TFO socket after it has been accepted.
//
// First read() will return all the data and this is consistent
// with the non-TFO case. Second read will return -1
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:10(10) win 32792 <mss 1460,sackOK,nop,nop,FO TFO_COOKIE,nop,nop>
+0 > S. 0:0(0) ack 11 <mss 1460,nop,nop,sackOK>
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }%
+0 %{ assert tcpi_state == TCP_SYN_RECV, tcpi_state }%
// 1st read will return the data from SYN.
// tcp_reset() sets sk->sk_err to ECONNRESET for SYN_RECV.
+0 < R. 11:11(0) win 32792
+0 %{ assert tcpi_state == TCP_CLOSE, tcpi_state }%
// This one w/o ACK bit will cause the same effect.
// +0 < R 11:11(0) win 32792
// See Step 2 in tcp_validate_incoming().
// found_ok_skb in tcp_recvmsg_locked()
+0 read(4, ..., 512) = 10
// !copied && sk->sk_err -> sock_error(sk)
+0 read(4, ..., 512) = -1 ECONNRESET (Connection reset by peer)
+0 close(4) = 0

View File

@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0
//
// Send a RST to a TFO socket before it is accepted.
//
// The socket won't go away and after it's accepted the data
// in the SYN pkt can still be read. But that's about all that
// the acceptor can do with the socket.
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:10(10) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7,FO TFO_COOKIE,nop,nop>
+0 > S. 0:0(0) ack 11 <mss 1460,nop,nop,sackOK,nop,wscale 8>
// 1st read will return the data from SYN.
+0 < R. 11:11(0) win 257
// This one w/o ACK bit will cause the same effect.
// +0 < R 11:11(0) win 257
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }%
+0 %{ assert tcpi_state == TCP_CLOSE, tcpi_state }%
+0 read(4, ..., 512) = 10
+0 read(4, ..., 512) = -1 ECONNRESET (Connection reset by peer)
+0 close(4) = 0

View File

@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0
//
// Send a RST to a TFO socket after it is accepted.
//
// The socket will change to TCP_CLOSE state with pending data so
// write() will fail. Pending data can be still be read and close()
// won't trigger RST if data is not read
//
// 565b7b2d2e63 ("tcp: do not send reset to already closed sockets")
// https://lore.kernel.org/netdev/4C1A2502.1030502@openvz.org/
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:10(10) win 32792 <mss 1460,sackOK,nop,nop, FO TFO_COOKIE,nop,nop>
+0 > S. 0:0(0) ack 11 <mss 1460,nop,nop,sackOK>
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }%
+0 %{ assert tcpi_state == TCP_SYN_RECV, tcpi_state }%
// tcp_done() sets sk->sk_state to TCP_CLOSE and clears tp->fastopen_rsk
+0 < R. 11:11(0) win 32792
+0 %{ assert tcpi_state == TCP_CLOSE, tcpi_state }%
+0 write(4, ..., 100) = -1 ECONNRESET(Connection reset by peer)
+0 close(4) = 0

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0
//
// Send a RST to a fully established socket with pending data before
// it is accepted.
//
// The socket with pending data won't go away and can still be accepted
// with data read. But it will be in TCP_CLOSE state.
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
// Invalid cookie, so accept() fails.
+0 < S 0:10(10) win 32792 <mss 1460,sackOK,nop,nop,FO aaaaaaaaaaaaaaaa,nop,nop>
+0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK, FO TFO_COOKIE,nop,nop>
+0 accept(3, ..., ...) = -1 EAGAIN (Resource temporarily unavailable)
// Complete 3WHS and send data and RST
+0 < . 1:1(0) ack 1 win 32792
+0 < . 1:11(10) ack 1 win 32792
+0 < R. 11:11(0) win 32792
// A valid reset won't make the fully-established socket go away.
// It's just that the acceptor will get a dead, unusable socket
// in TCP_CLOSE state.
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }%
+0 %{ assert tcpi_state == TCP_CLOSE, tcpi_state }%
+0 write(4, ..., 100) = -1 ECONNRESET(Connection reset by peer)
+0 read(4, ..., 512) = 10
+0 read(4, ..., 512) = 0

View File

@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0
//
// Test the server cookie is generated by aes64 encoding of remote and local
// IP addresses with a master key specified via sockopt TCP_FASTOPEN_KEY
//
`./defaults.sh
./set_sysctls.py /proc/sys/net/ipv4/tcp_fastopen_key=00000000-00000000-00000000-00000000`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
// Set a key of a1a1a1a1-b2b2b2b2-c3c3c3c3-d4d4d4d4 (big endian).
// This would produce a cookie of TFO_COOKIE like many other
// tests (which the same key but set via sysctl).
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN_KEY,
"\xa1\xa1\xa1\xa1\xb2\xb2\xb2\xb2\xc3\xc3\xc3\xc3\xd4\xd4\xd4\xd4", 16) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
// Request a valid cookie TFO_COOKIE
+0 < S 1428932:1428942(10) win 10000 <mss 1012,nop,nop,FO,sackOK,TS val 1 ecr 0,nop,wscale 7>
+0 > S. 0:0(0) ack 1428933 <mss 1460,sackOK,TS val 10000 ecr 1,nop,wscale 8,FO TFO_COOKIE,nop,nop>
+0 < . 1:1(0) ack 1 win 257 <nop,nop,TS val 2 ecr 10000>
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }%
+0 close(4) = 0
+0 > F. 1:1(0) ack 1 <nop,nop,TS val 10001 ecr 2>
+0 < F. 1:1(0) ack 2 win 257 <nop,nop,TS val 3 ecr 10001>
+0 > . 2:2(0) ack 2 <nop,nop,TS val 10002 ecr 3>
+0 close(3) = 0
// Restart the listener
+0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
// Test setting the key in the listen state, and produces an identical cookie
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN_KEY,
"\xa1\xa1\xa1\xa1\xb2\xb2\xb2\xb2\xc3\xc3\xc3\xc3\xd4\xd4\xd4\xd4", 16) = 0
+0 < S 6814000:6815000(1000) win 10000 <mss 1012,nop,nop,FO TFO_COOKIE,sackOK,TS val 10 ecr 0,nop,wscale 7>
+0 > S. 0:0(0) ack 6815001 <mss 1460,sackOK,TS val 10000 ecr 10,nop,wscale 8>
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }%
+0 < . 1001:1001(0) ack 1 win 257 <nop,nop,TS val 12 ecr 10000>
+0 read(4, ..., 8192) = 1000
+0 close(4) = 0
+0 > F. 1:1(0) ack 1001 <nop,nop,TS val 10101 ecr 12>
+0 < F. 1001:1001(0) ack 2 win 257 <nop,nop,TS val 112 ecr 10101>
+0 > . 2:2(0) ack 1002 <nop,nop,TS val 10102 ecr 112>
+0 close(3) = 0
// Restart the listener
+0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
// Test invalid key length (must be 16 bytes)
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN_KEY, "", 0) = -1 (Invalid Argument)
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN_KEY, "", 3) = -1 (Invalid Argument)
// Previous cookie won't be accepted b/c this listener uses the global key (0-0-0-0)
+0 < S 6814000:6815000(1000) win 10000 <mss 1012,nop,nop,FO TFO_COOKIE,sackOK,TS val 10 ecr 0,nop,wscale 7>
+0 > S. 0:0(0) ack 6814001 <mss 1460,sackOK,TS val 10000 ecr 10,nop,wscale 8,FO TFO_COOKIE_ZERO,nop,nop>

View File

@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
//
// Close a listener socket with pending TFO child.
// This will trigger RST pkt to go out.
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:10(10) win 32792 <mss 1460,sackOK,nop,nop,FO TFO_COOKIE,nop,nop>
+0 > S. 0:0(0) ack 11 <mss 1460,nop,nop,sackOK>
// RST pkt is generated for each not-yet-accepted TFO child.
// inet_csk_listen_stop() -> inet_child_forget() -> tcp_disconnect()
// -> tcp_need_reset() is true for SYN_RECV
+0 close(3) = 0
+0 > R. 1:1(0) ack 11

View File

@ -1,26 +1,30 @@
// SPDX-License-Identifier: GPL-2.0
`./defaults.sh
./set_sysctls.py /proc/sys/net/ipv4/tcp_fastopen=0x602 /proc/sys/net/ipv4/tcp_timestamps=0`
./set_sysctls.py /proc/sys/net/ipv4/tcp_timestamps=0`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:10(10) win 32792 <mss 1460,nop,nop,sackOK>
+0 < S 0:10(10) win 32792 <mss 1460,nop,nop,sackOK,nop,nop,FO TFO_COOKIE>
+0 > S. 0:0(0) ack 11 win 65535 <mss 1460,nop,nop,sackOK>
// sk->sk_state is TCP_SYN_RECV
+.1 accept(3, ..., ...) = 4
+0 accept(3, ..., ...) = 4
+0 %{ assert tcpi_state == TCP_SYN_RECV, tcpi_state }%
// tcp_disconnect() sets sk->sk_state to TCP_CLOSE
+0 connect(4, AF_UNSPEC, ...) = 0
+0 > R. 1:1(0) ack 11 win 65535
+0 %{ assert tcpi_state == TCP_CLOSE, tcpi_state }%
// connect() sets sk->sk_state to TCP_SYN_SENT
+0 fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 connect(4, ..., ...) = -1 EINPROGRESS (Operation is now in progress)
+0 > S 0:0(0) win 65535 <mss 1460,nop,nop,sackOK,nop,wscale 8>
+0 %{ assert tcpi_state == TCP_SYN_SENT, tcpi_state }%
// tp->fastopen_rsk must be NULL
+1 > S 0:0(0) win 65535 <mss 1460,nop,nop,sackOK,nop,wscale 8>

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0
//
// Close a TFO socket with unread data.
// This will trigger a RST pkt.
`./defaults.sh`
0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 setsockopt(3, SOL_TCP, TCP_FASTOPEN, [1], 4) = 0
+0 < S 0:10(10) win 32792 <mss 1460,sackOK,nop,nop,FO TFO_COOKIE,nop,nop>
+0 > S. 0:0(0) ack 11 <mss 1460,nop,nop,sackOK>
+0 accept(3, ..., ...) = 4
+0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }%
+0 %{ assert tcpi_state == TCP_SYN_RECV, tcpi_state }%
// data_was_unread == true in __tcp_close()
+0 close(4) = 0
+0 > R. 1:1(0) ack 11