When a TCP connection is in FIN-WAIT-1 state with the FIN packet blocked in the send buffer, and the peer continuously sends zero-window advertisements, the current implementation reset the zero-window probe timer while maintaining the current `icsk->icsk_backoff`, causing the connection to remain permanently in FIN-WAIT-1 state. Reproduce conditions: 1. Peer's receive window is full and actively sending continuous zero window advertisements. 2. Local FIN packet is blocked in send buffer due to peer's zero-window. 3. Local socket has been closed (entered orphan state). The root cause lies in the tcp_ack_probe() function: when receiving a zero-window ACK, - It reset the probe timer while keeping the current `icsk->icsk_backoff`. - This would result in the condition `icsk->icsk_backoff >= max_probes` false. - Orphaned socket cannot be set to close. This patch modifies the tcp_ack_probe() logic: when the socket is dead, upon receiving a zero-window packet, instead of resetting the probe timer, we maintain the current timer, ensuring the probe interval grows according to 'icsk->icsk_backoff', thus causing the zero-window probe timer to eventually timeout and close the socket. Signed-off-by: HaiYang Zhong --- net/ipv4/tcp_input.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 71b76e98371a..22fc82cb6b73 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3440,6 +3440,8 @@ static void tcp_ack_probe(struct sock *sk) } else { unsigned long when = tcp_probe0_when(sk, tcp_rto_max(sk)); + if (sock_flag(sk, SOCK_DEAD) && icsk->icsk_backoff != 0) + return; when = tcp_clamp_probe0_to_user_timeout(sk, when); tcp_reset_xmit_timer(sk, ICSK_TIME_PROBE0, when, true); } -- 2.43.7 Add packetdrill test to reproduce and verify the permanent FIN-WAIT-1 state issue when continuous zero window packets are received. The test simulates: - TCP connection establishment - Peer advertising zero window - Local FIN blocked in send buffer due to zero window - Continuous zero window ACKs from peer - Verification of connection timeout (after fix) Signed-off-by: HaiYang Zhong --- .../net/tcp_fin_wait1_zero_window.pkt | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 tools/testing/selftests/net/tcp_fin_wait1_zero_window.pkt diff --git a/tools/testing/selftests/net/tcp_fin_wait1_zero_window.pkt b/tools/testing/selftests/net/tcp_fin_wait1_zero_window.pkt new file mode 100644 index 000000000000..86ceb95de744 --- /dev/null +++ b/tools/testing/selftests/net/tcp_fin_wait1_zero_window.pkt @@ -0,0 +1,58 @@ +// Test for permanent FIN-WAIT-1 state with continuous zero-window advertisements +// Author: HaiYang Zhong + + +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 65535 +0.100 > S. 0:0(0) ack 1 +0.100 < . 1:1(0) ack 1 win 65535 +0.100 accept(3, ..., ...) = 4 + +// Send data to fill receive window +0.200 write(4, ..., 5) = 5 +0.200 > P. 1:6(5) ack 1 + +// Advertise zero-window +0.200 < . 1:1(0) ack 6 win 0 + +// Application closes connection, sends FIN (but blocked by zero window) +0.200 close(4) = 0 + +//Send zero-window probe packet ++0.200 > . 5:5(0) ack 1 ++0.400 > . 5:5(0) ack 1 ++0.800 > . 5:5(0) ack 1 ++1.600 > . 5:5(0) ack 1 ++3.200 > . 5:5(0) ack 1 ++6.400 > . 5:5(0) ack 1 ++12.800 > . 5:5(0) ack 1 + +// Continuously sending zero-window ACKs +30.000 < . 1:1(0) ack 6 win 0 + +// Key verification points +// Without fix: waiting for packet timeout due to timer reset +// With fix: this probe is sent as scheduled ++22.000~+23.000 > . 5:5(0) ack 1 + +// More zero-window ACKs from peer +60.000 < . 1:1(0) ack 6 win 0 +90.000 < . 1:1(0) ack 6 win 0 ++16.000~+19.000 > . 5:5(0) ack 1 +120.000 < . 1:1(0) ack 6 win 0 +150.000 < . 1:1(0) ack 6 win 0 +180.000 < . 1:1(0) ack 6 win 0 +210.000 < . 1:1(0) ack 6 win 0 ++0.000~+5.000 > . 5:5(0) ack 1 +240.000 < . 1:1(0) ack 6 win 0 +270.000 < . 1:1(0) ack 6 win 0 +300.000 < . 1:1(0) ack 6 win 0 +330.000 < . 1:1(0) ack 6 win 0 +360.000 < . 1:1(0) ack 6 win 0 + +// Connection reset after zero-window probe timeout ++0.000 > R 6:6(0) -- 2.43.7