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