In tcp_send_syn_data(), the TCP Fast Open client could give up embedding payload into SYN, but the behaviour is inconsistent. 1. Send a bare SYN with TFO request (option w/o cookie) 2. Send a bare SYN with TFO cookie When the client does not have a valid cookie, a bare SYN is sent with the TFO option without a cookie. When sendmsg(MSG_FASTOPEN) is called with zero payload and the client has a valid cookie, a bare SYN is sent with the TFO cookie, which is confusing. This also happens when tcp_wmem_schedule() fails to charge non-zero payload. OTOH, other fallback paths align with 1. In this case, a TFO request is not strictly needed as tcp_fastopen_cookie_check() has succeeded, but we can use this round to refresh the TFO cookie. Let's avoid sending TFO cookie w/o payload to make fallback behaviour consistent. Signed-off-by: Kuniyuki Iwashima --- net/ipv4/tcp_output.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index bb3576ac0ad7d..2847c1ffa1615 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -4151,6 +4151,9 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn) if (!tcp_fastopen_cookie_check(sk, &tp->rx_opt.mss_clamp, &fo->cookie)) goto fallback; + if (!fo->size) + goto fallback; + /* MSS for SYN-data is based on cached MSS and bounded by PMTU and * user-MSS. Reserve maximum option space for middleboxes that add * private TCP options. The cost is reduced data space in SYN :( @@ -4164,33 +4167,33 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn) space = min_t(size_t, space, fo->size); - if (space && - !skb_page_frag_refill(min_t(size_t, space, PAGE_SIZE), + if (!skb_page_frag_refill(min_t(size_t, space, PAGE_SIZE), pfrag, sk->sk_allocation)) goto fallback; + syn_data = tcp_stream_alloc_skb(sk, sk->sk_allocation, false); if (!syn_data) goto fallback; + memcpy(syn_data->cb, syn->cb, sizeof(syn->cb)); - if (space) { - space = min_t(size_t, space, pfrag->size - pfrag->offset); - space = tcp_wmem_schedule(sk, space); - } - if (space) { + + space = min_t(size_t, space, pfrag->size - pfrag->offset); + space = tcp_wmem_schedule(sk, space); + if (space) space = copy_page_from_iter(pfrag->page, pfrag->offset, space, &fo->data->msg_iter); - if (unlikely(!space)) { - tcp_skb_tsorted_anchor_cleanup(syn_data); - kfree_skb(syn_data); - goto fallback; - } - skb_fill_page_desc(syn_data, 0, pfrag->page, - pfrag->offset, space); - page_ref_inc(pfrag->page); - pfrag->offset += space; - skb_len_add(syn_data, space); - skb_zcopy_set(syn_data, fo->uarg, NULL); + if (unlikely(!space)) { + tcp_skb_tsorted_anchor_cleanup(syn_data); + kfree_skb(syn_data); + goto fallback; } + + skb_fill_page_desc(syn_data, 0, pfrag->page, pfrag->offset, space); + page_ref_inc(pfrag->page); + pfrag->offset += space; + skb_len_add(syn_data, space); + skb_zcopy_set(syn_data, fo->uarg, NULL); + /* No more data pending in inet_wait_for_connect() */ if (space == fo->size) fo->data = NULL; -- 2.51.0.788.g6d19910ace-goog These two TFO client tests are imported from google/packetdrill on GitHub: * client_nonblocking-sendto-errnos.pkt * nonblocking-sendto-empty-buf.pkt Both files had tests for 0 payload sendto(MSG_FASTOPEN) assuming that SYN will be sent w/ TFO cookie if cached, +0 sendto(4, ..., 0, MSG_FASTOPEN, ..., ...) = -1 EINPROGRESS (Operation is now in progress) +0 > S 0:0(0) and now it fails with the previous patch, so this part is fixed up in both files. In addition, the former had lengthy 6s wait in the last test case to ensure that there will not be multiple SYN retransmissions, and I changed it to 2s because now linear timeout is enabled by default (it can be reduced further to 1s, but 2s just in case to avoid flakiness). Signed-off-by: Kuniyuki Iwashima --- ...en_client_nonblocking-sendto-empty-buf.pkt | 45 +++++++ ...topen_client_nonblocking-sendto-errnos.pkt | 125 ++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 tools/testing/selftests/net/packetdrill/tcp_fastopen_client_nonblocking-sendto-empty-buf.pkt create mode 100644 tools/testing/selftests/net/packetdrill/tcp_fastopen_client_nonblocking-sendto-errnos.pkt diff --git a/tools/testing/selftests/net/packetdrill/tcp_fastopen_client_nonblocking-sendto-empty-buf.pkt b/tools/testing/selftests/net/packetdrill/tcp_fastopen_client_nonblocking-sendto-empty-buf.pkt new file mode 100644 index 0000000000000..6ebeff7c5857e --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_fastopen_client_nonblocking-sendto-empty-buf.pkt @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Non-blocking Fast Open with an empty buffer +// +`./defaults.sh + ./set_sysctls.py /proc/sys/net/ipv4/tcp_timestamps=0` + + 0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 + +0 sendto(3, ..., 0, MSG_FASTOPEN, ..., ...) = -1 EINPROGRESS (Operation is now in progress) + +0 > S 0:0(0) + +0 < S. 123:123(0) ack 1 win 14600 + +0 > . 1:1(0) ack 1 + +0 close(3) = 0 + +0 > F. 1:1(0) ack 1 + +0 < F. 1:1(0) ack 2 win 92 + +0 > . 2:2(0) ack 2 + + +// +// Test: non-blocking sendto() of 0B +// + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 4 + +0 sendto(4, ..., 0, MSG_FASTOPEN, ..., ...) = -1 EINPROGRESS (Operation is now in progress) + +// TFO cookie (FO abcd1234) is not sent w/o payload + +0 > S 0:0(0) + +// Server acks FO and replies a different MSS (940B) + +0 < S. 1234:1234(0) ack 1 win 14600 + +0 > . 1:1(0) ack 1 + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }% + + +// +// Test: previous server's MSS (940B) and cookie are still cached +// + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 5 + +0 sendto(5, ..., 2000, MSG_FASTOPEN, ..., ...) = 900 + +0 > S 0:900(900) +// Sever acknowledges the data but also sends new cookie + +0 < S. 5678:5678(0) ack 901 win 14600 + +0 > . 901:901(0) ack 1 + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }% + +`/tmp/sysctl_restore_${PPID}.sh` diff --git a/tools/testing/selftests/net/packetdrill/tcp_fastopen_client_nonblocking-sendto-errnos.pkt b/tools/testing/selftests/net/packetdrill/tcp_fastopen_client_nonblocking-sendto-errnos.pkt new file mode 100644 index 0000000000000..2f74880fe3bbc --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_fastopen_client_nonblocking-sendto-errnos.pkt @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Test non-blocking sendto(MSG_FASTOPEN) errno(s). +// +`./defaults.sh + ./set_sysctls.py /proc/sys/net/ipv4/tcp_timestamps=0 \ + /proc/sys/net/ipv4/tcp_fastopen_blackhole_timeout_sec=0` + +/////////////////////////////////////////////////////////////////////////////// +// Non-blocking errnos +// + 0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 + +0 `sysctl -q net.ipv4.tcp_fastopen=0` +// +// Test: EOPNOTSUPP if fastopen is disabled +// + +0 sendto(3, ..., 1000, MSG_FASTOPEN, ..., ...) = -1 EOPNOTSUPP (Operation not supported) + +0 `sysctl -q net.ipv4.tcp_fastopen=1` + + +// +// Test: 0-byte sendto() returns EINPROGRESS when no cookie is in cache +// + +0 sendto(3, ..., 0, MSG_FASTOPEN, ..., ...) = -1 EINPROGRESS (Operation now in progress) + +0 > S 0:0(0) + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }% + +0 close(3) = 0 + + + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 4 +// +// Test: 1000-byte sendto() returns EINPROGRESS when no cookie is in cache +// + +0 sendto(4, ..., 1000, MSG_FASTOPEN|MSG_DONTWAIT, ..., ...) = -1 EINPROGRESS (Operation now in progress) + +0 > S 0:0(0) +// +// Test: EALREADY on multiple sendto(MSG_FASTOPEN) in SYN-SENT +// + +0 sendto(4, ..., 1000, MSG_FASTOPEN, ..., ...) = -1 EALREADY (Operation already in progress) +// +// Test: EAGAIN on write() in SYN-SENT +// + +0 write(4, ..., 1000) = -1 EAGAIN (Resource temporarily unavailable) + +0 < S. 0:0(0) ack 1 win 5840 + +0 > . 1:1(0) ack 1 + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }% + +0 close(4) = 0 + + +// +// Repeat previous tests with a valid cookie cached locally +// + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 5 +// +// Test: *NO* EINPROGRESS in SYN-SENT b/c data are buffered and transmitted +// + +0 sendto(5, ..., 1000, MSG_FASTOPEN, ..., ...) = 1000 + +0 > S 0:1000(1000) +// +// Test: EALREADY on multiple sendto(MSG_FASTOPEN) in SYN-SENT +// + +0 sendto(5, ..., 1000, MSG_FASTOPEN, ..., ...) = -1 EALREADY (Operation already in progress) +// +// Test: EAGAIN on write() in SYN-SENT +// + +0 write(5, ..., 1000) = -1 EAGAIN (Resource temporarily unavailable) + +0 < S. 506036:506036(0) ack 1001 win 5840 + +0 > . 1001:1001(0) ack 1 + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }% + +0 close(5) = 0 + + +// +// Test: a 0-byte sendto() returns EINPROGRESS even if cookie is in the cache. +// since sendto(MSG_FASTOPEN) is a connect and write combo, and a null +// write is a no-op, so it should behave like a normal connect() +// + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 6 + +0 sendto(6, ..., 0, MSG_FASTOPEN, ..., ...) = -1 EINPROGRESS (Operation now in progress) + +0 > S 0:0(0) + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }% + +0 close(6) = 0 + + +// +// Test: ECONNREFUSED when remote resets on SYN +// + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 7 + +0 sendto(7, ..., 2000, MSG_FASTOPEN, ..., ...) = 1420 + +0 > S 0:1420(1420) + +0 < R. 0:0(0) ack 1 win 0 + +0 write(7, ..., 2000) = -1 ECONNREFUSED (Connection Refused) + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }% + +0 close(7) = 0 + + +// +// Test: ECONNRESET if RST is received after SYN +// + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 8 + +0 sendto(8, ..., 1420, MSG_FASTOPEN, ..., ...) = 1420 + +0 > S 0:1420(1420) + +0 < S. 0:0(0) ack 1421 win 5840 + +0 > . 1421:1421(0) ack 1 + +0 < R. 1:1(0) ack 1421 win 0 + +0 write(8, ..., 2000) = -1 ECONNRESET (Connection reset by peer) + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }% + + +// +// Test: ETIMEOUT when SYN timed out +// + +0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 9 + +0 sendto(9, ..., 2000, MSG_FASTOPEN|MSG_DONTWAIT, ..., ...) = 1420 +// Retry once to make this test shorter. + +0 setsockopt(9, IPPROTO_TCP, TCP_SYNCNT, [1], 4) = 0 + +0 > S 0:1420(1420) + +1 > S 0:0(0) +// Why wait 2 sec? it's a bug fixed in 4d22f7d372f5 +// https://bugzilla.redhat.com/show_bug.cgi?id=688989 +// Originally, it was 6 sec but now 2 sec thanks to linear timeout + +2 write(9, ..., 2000) = -1 ETIMEDOUT (Connection timed out) + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }% + +`/tmp/sysctl_restore_${PPID}.sh` -- 2.51.0.788.g6d19910ace-goog Currently, SYN+ACK payload is acknowledged and queued even for a TFO fallback client, which did not send SYN with payload. For example, this packetdrill script does not fail, even though the server does not send a TFO cookie. 0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 +0 sendto(3, ..., 0, MSG_FASTOPEN, ..., ...) = -1 EINPROGRESS (Operation is now in progress) +0 > S 0:0(0) +0 < S. 0:1000(1000) ack 1 win 5840 +0 > . 1:1(0) ack 1001 // should be ack 1 +0 read(3, ..., 1000) = 1000 // should fail with -EAGAIN This is because tcp_rcv_fastopen_synack(), which handles SYN+ACK for both TFO client and TFO fallback client, calls tcp_fastopen_add_skb() unconditionally. RFC 7413 (TCP Fast Open), in Section 3. Protocol Overview [0], states that the SYN+ACK payload is only allowed when the server acknowledges SYN data: 3. If the server accepts the data in the SYN packet, it may send the response data before the handshake finishes. Let's not call tcp_fastopen_add_skb() when the client did not send SYN with payload. Note that Linux does not send SYN+ACK with payload but FreeBSD could as mentioned in the commit below. [1] Link: https://datatracker.ietf.org/doc/html/rfc7413#section-3 #[0] Link: https://cgit.freebsd.org/src/commit/?id=3f43239f21e357246696f1e8675178881d9ed5bc #[1] Fixes: 61d2bcae99f66 ("tcp: fastopen: accept data/FIN present in SYNACK message") Signed-off-by: Kuniyuki Iwashima --- net/ipv4/tcp_input.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 8fc97f4d8a6b2..e1d3066782b57 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6552,6 +6552,9 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, tcp_fastopen_cache_set(sk, mss, cookie, syn_drop, try_exp); + if (!tp->syn_data) + return false; + if (data) { /* Retransmit unacked data in SYN */ if (tp->total_retrans) tp->fastopen_client_fail = TFO_SYN_RETRANSMITTED; @@ -6564,16 +6567,14 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, LINUX_MIB_TCPFASTOPENACTIVEFAIL); return true; } - tp->syn_data_acked = tp->syn_data; - if (tp->syn_data_acked) { - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPFASTOPENACTIVE); - /* SYN-data is counted as two separate packets in tcp_ack() */ - if (tp->delivered > 1) - --tp->delivered; - } - tcp_fastopen_add_skb(sk, synack); + /* SYN-data is counted as two separate packets in tcp_ack() */ + if (tp->delivered > 1) + --tp->delivered; + tp->syn_data_acked = 1; + tcp_fastopen_add_skb(sk, synack); + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPFASTOPENACTIVE); return false; } -- 2.51.0.788.g6d19910ace-goog client_synack-data.pkt tests various TFO client scenarios for SYN+ACK payload processing, which never happen with Linux server. In addition to the common changes mentioned in the cover letter, the following changes are added to the original script: 1. Add payload to SYN+ACK for TFO fallback client to cover the previous patch 2. Add TCPI_OPT_SYN_DATA assertion in each test case 3. Add TcpExtPAWSActive check in the last test case Signed-off-by: Kuniyuki Iwashima --- .../tcp_fastopen_client_synack-data.pkt | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 tools/testing/selftests/net/packetdrill/tcp_fastopen_client_synack-data.pkt diff --git a/tools/testing/selftests/net/packetdrill/tcp_fastopen_client_synack-data.pkt b/tools/testing/selftests/net/packetdrill/tcp_fastopen_client_synack-data.pkt new file mode 100644 index 0000000000000..c49cfd3d491e5 --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_fastopen_client_synack-data.pkt @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Test server sending SYNACK with data +// +--tcp_ts_ecr_scaled // used in TEST 5 + +`./defaults.sh + ./set_sysctls.py /proc/sys/net/ipv4/tcp_timestamps=0` + + +// +// Cache warmup: send a Fast Open cookie request +// SYN-ACK payload must not be ACKed +// + 0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 + +0 sendto(3, ..., 0, MSG_FASTOPEN, ..., ...) = -1 EINPROGRESS (Operation is now in progress) + +0 > S 0:0(0) + +0 < S. 123:133(10) ack 1 win 5840 +// SYN+ACK data cannot be ACKed for TFO fallback client + +0 > . 1:1(0) ack 1 + + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) == 0, tcpi_options }% + +0 read(3, ..., 1000) = -1 EAGAIN (Resource temporarily unavailable) + +0 close(3) = 0 + +0 > F. 1:1(0) ack 1 + +.01 < F. 1:1(0) ack 2 win 92 + +0 > . 2:2(0) ack 2 + + +// +// TEST1: Servers sends SYN-ACK with data and another two data packets +// + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 4 + +0 sendto(4, ..., 1000, MSG_FASTOPEN, ..., ...) = 1000 + +0 > S 0:1000(1000) + +0 < S. 1000000:1001400(1400) ack 1001 win 5840 + +0 < . 1401:2801(1400) ack 1001 win 257 + +0 < P. 2801:3001(200) ack 1001 win 257 + +0 > . 1001:1001(0) ack 1401 + +0 > . 1001:1001(0) ack 2801 + +0 > . 1001:1001(0) ack 3001 + + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }% + +0 read(4, ..., 100000) = 3000 + +0 close(4) = 0 + +0 > F. 1001:1001(0) ack 3001 + +.01 < F. 3001:3001(0) ack 1002 win 257 + +0 > . 1002:1002(0) ack 3002 + + +// +// TEST2: SYN-ACK-DATA-FIN is accepted. state SYN_SENT -> CLOSE_WAIT. +// poll() functions correctly. +// + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 4 + +0 sendto(4, ..., 1000, MSG_FASTOPEN, ..., ...) = 1000 + +0...0.010 poll([{fd=4, + events=POLLIN|POLLOUT|POLLERR|POLLRDHUP, + revents=POLLIN|POLLOUT|POLLRDHUP}], 1, 100) = 1 + +0 > S 0:1000(1000) + +.01 < SF. 1000000:1001400(1400) ack 1001 win 5840 + + +0 %{ assert tcpi_state == TCP_CLOSE_WAIT, tcpi_state }% + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }% + +0 read(4, ..., 100000) = 1400 + +0 read(4, ..., 100000) = 0 + +0 > . 1001:1001(0) ack 1402 + +0 close(4) = 0 + +0 > F. 1001:1001(0) ack 1402 + +.01 < . 1402:1402(0) ack 1002 win 257 + + +// +// TEST3: Servers sends SYN-ACK with data and another two data packets. SYN-ACK +// is lost and the two data packets are ignored. Client timed out and +// retransmitted SYN. +// + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 4 + +0 sendto(4, ..., 1000, MSG_FASTOPEN, ..., ...) = 1000 + +0 > S 0:1000(1000) + +.01 < . 1401:2801(1400) ack 1001 win 257 + +0 < P. 2801:3001(200) ack 1001 win 257 + +// SYN timeout + +.99~+1.1 > S 0:0(0) + +.01 < S. 1000000:1001400(1400) ack 1001 win 5840 + +0 > . 1001:1001(0) ack 1401 + +.01 < . 1401:2801(1400) ack 1001 win 257 + +0 > . 1001:1001(0) ack 2801 + +0 < P. 2801:3001(200) ack 1001 win 257 + +0 > . 1001:1001(0) ack 3001 + + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }% + +0 read(4, ..., 100000) = 3000 + +0 close(4) = 0 + +0 > F. 1001:1001(0) ack 3001 + +.1 < F. 3001:3001(0) ack 1002 win 257 + +0 > . 1002:1002(0) ack 3002 + + +// +// TEST4: SYN-ACK-DATA with TS opt. Also test poll() +// + +0 `sysctl -q net.ipv4.tcp_timestamps=1` + + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 4 + +0 sendto(4, ..., 1000, MSG_FASTOPEN, ..., ...) = 1000 + +0...0.010 poll([{fd=4, + events=POLLIN|POLLOUT|POLLERR, + revents=POLLIN|POLLOUT}], 1, 100) = 1 + +0 > S 0:1000(1000) + +.01 < S. 1000000:1001400(1400) ack 1001 win 5840 + +0 > . 1001:1001(0) ack 1401 + +0 < . 1401:2801(1400) ack 1001 win 257 + +0 > . 1001:1001(0) ack 2801 + +0 < P. 2801:3001(200) ack 1001 win 257 + +0 > . 1001:1001(0) ack 3001 + + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }% + +0 read(4, ..., 100000) = 3000 + +0 close(4) = 0 + +0 > F. 1001:1001(0) ack 3001 + +.01 < F. 3001:3001(0) ack 1002 win 257 + +0 > . 1002:1002(0) ack 3002 + + +// +// TEST5: SYN-ACK-DATA with bad TS opt is repelled with an RST. +// + +0 `nstat > /dev/null` + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 4 + +0 sendto(4, ..., 1000, MSG_FASTOPEN, ..., ...) = 1000 + +0 > S 0:1000(1000) + +// bad ECR value is rejected as LINUX_MIB_PAWSACTIVEREJECTED + +.01 < S. 1000000:1001400(1400) ack 1001 win 5840 + +0 > R 1001:1001(0) + +// A later valid SYN establishes the connection + +.01 < S. 1000000:1000100(100) ack 1001 win 5840 + +0 > . 1001:1001(0) ack 101 + +// Make sure the RST above incremented LINUX_MIB_PAWSACTIVEREJECTED + +0 `nstat | grep -q TcpExtPAWSActive` + + +0 %{ assert (tcpi_options & TCPI_OPT_SYN_DATA) != 0, tcpi_options }% + +0 read(4, ..., 100000) = 100 + +0 %{ assert tcpi_state == TCP_ESTABLISHED, tcpi_state }% + +`/tmp/sysctl_restore_${PPID}.sh` \ No newline at end of file -- 2.51.0.788.g6d19910ace-goog