When connection migration happens in tcp_v[46]_rcv(), a new listener is passed to tcp_check_req(). When either of the following is true, * the new listener queue is full and net.ipv4.tcp_abort_on_overflow is set * the packet has RST or (not stripped) SYN tcp_check_req() sends RST and calls inet_csk_reqsk_queue_drop(). Here, the new listener is passed, and its qlen is decremented wrongly while reqsk is tied to the old listener. Once qlen underflows, inet_csk_reqsk_queue_is_full() always return true, and the new listener stops serving. Without the patch, this script times out while waiting for SYN+ACK since the new listener drops SYN, but with the patch, 3WHS completes. 0 `sysctl -q net.ipv4.tcp_migrate_req=1` +0 `sysctl -q net.ipv4.tcp_syncookies=0` +0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 +0 setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0 +0 bind(3, ..., ...) = 0 +0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 4 +0 setsockopt(4, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0 +0 bind(4, ..., ...) = 0 +0 listen(4, 128) = 0 +0 < S 0:0(0) win 32792 +0 > S. 0:0(0) ack 1 <...> +0 listen(3, 128) = 0 +0 close(4) = 0 +.01 < R 1:1(0) win 0 +0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 5 +0 listen(5, 1) = 0 +.01 < S 0:0(0) win 32792 +0 > S. 0:0(0) ack 1 <...> +0 < . 1:1(0) ack 1 win 32792 +0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0 +0 accept(3, ..., ...) = 6 Let's pass req->rsk_listener instead. Fixes: d4f2c86b2b7e ("tcp: Migrate TCP_NEW_SYN_RECV requests at receiving the final ACK.") Signed-off-by: Kuniyuki Iwashima --- one-shot bug will not be worth a new test file. --- net/ipv4/tcp_minisocks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 0ddfd5af6e58..a05bb02e204c 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -974,7 +974,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, tcp_reset(sk, skb); } if (!fastopen) { - bool unlinked = inet_csk_reqsk_queue_drop(sk, req); + bool unlinked = inet_csk_reqsk_queue_drop(req->rsk_listener, req); if (unlinked) __NET_INC_STATS(sock_net(sk), LINUX_MIB_EMBRYONICRSTS); -- 2.56.0.611.g1030f0b940-goog