net/ipv4/tcp_input.c | 2 ++ 1 file changed, 2 insertions(+)
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 <wokezhong@tencent.com>
---
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
On Fri, Sep 19, 2025 at 10:30 AM HaiYang Zhong <wokezhong@gmail.com> wrote:
>
> 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 <wokezhong@tencent.com>
> ---
> 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
Hi there. Seems reasonable, but could you provide a packetdrill test ?
Also, what if the FIN was already sent, but the peer retracted its RWIN ?
tcp_ack_probe() would return early (if (!head) return;)
Hi Eric,
Thank you for your review and suggestions. As requested, I've added a packetdrill test to reproduce and verify the issue.
Changes in v2:
- Added packetdrill test in tools/testing/selftests/net/.
- The test reproduces the exact scenario described in the commit
message.
- Test verifies that the connection eventually times out after the fix.
The test covers:
1. TCP connection establishment.
2. Peer advertising zero window after receiving data.
3. Local close() with FIN blocked in send buffer.
4. Continuous zero window ACKs from peer.
5. Connection timeout verification.
About your question regarding the case where FIN was already sent but peer retracted RWIN:
In that scenario, peer will drop the FIN packet due to "TCPZeroWindowDrop". The FIN will be retransmitted by RTO, not by the zero-window probe timer.
I tested this with multiple kernel versions and the test reliably reproduces the issue before the fix and passes after the fix.
Thank you for your guidance!
Best regards,
HaiYang Zhong
HaiYang Zhong (2):
net/tcp: fix permanent FIN-WAIT-1 state with continuous zero window
packets
net/tcp: add packetdrill test for FIN-WAIT-1 zero-window fix
net/ipv4/tcp_input.c | 2 +
.../net/tcp_fin_wait1_zero_window.pkt | 58 +++++++++++++++++++
2 files changed, 60 insertions(+)
create mode 100644 tools/testing/selftests/net/tcp_fin_wait1_zero_window.pkt
--
2.43.7
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 <wokezhong@tencent.com>
---
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 <wokezhong@tencent.com>
---
.../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 <wokezhong@tencent.com>
+
+
+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 <mss 1460>
+0.100 > S. 0:0(0) ack 1 <mss 1460>
+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
On Thu, Oct 23, 2025 at 7:48 AM HaiYang Zhong <wokezhong@gmail.com> wrote: > > 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 <wokezhong@tencent.com> > --- > .../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 <wokezhong@tencent.com> > + > + > +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 <mss 1460> > +0.100 > S. 0:0(0) ack 1 <mss 1460> > +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) I guess this test will time out if you run via selftest ?
Hi Kuniyuki,
Thank you for the feedback. I've shortened the test duration in v3.
The test is now:
- Moved to tools/testing/selftests/net/packetdrill/
- Reduced from 360+ seconds to under 4 seconds
Please see the v3 series for details.
Best regards,
HaiYang Zhong
HaiYang Zhong (2):
net/tcp: fix permanent FIN-WAIT-1 state with continuous zero window
packets
net/tcp: add packetdrill test for FIN-WAIT-1 zero-window fix
net/ipv4/tcp_input.c | 2 ++
.../packetdrill/tcp_fin_wait1_zero_window.pkt | 34 +++++++++++++++++++
2 files changed, 36 insertions(+)
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt
--
2.43.7
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 <wokezhong@tencent.com>
---
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
Move the packetdrill test to the packetdrill directory and shorten
the test duration.
In the previous packetdrill test script, the long duration was due to
presenting the entire zero-window probe backoff process. The test has
been modified to only observe the first few packets to shorten the test
time while still effectively verifying the fix.
- Moved test to tools/testing/selftests/net/packetdrill/
- Reduced test duration from 360+ seconds to under 4 seconds
Signed-off-by: HaiYang Zhong <wokezhong@tencent.com>
---
.../packetdrill/tcp_fin_wait1_zero_window.pkt | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt
diff --git a/tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt b/tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt
new file mode 100644
index 000000000000..854ede56e7dd
--- /dev/null
+++ b/tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt
@@ -0,0 +1,34 @@
+// Test for permanent FIN-WAIT-1 state with continuous zero-window advertisements
+// Author: HaiYang Zhong <wokezhong@tencent.com>
+
+
+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 <mss 1460>
+0.100 > S. 0:0(0) ack 1 <mss 1460>
+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.000 < . 1:1(0) ack 6 win 0
+
+// Without fix: This probe won't match - timer was reset, probe will be sent 2.600s after the previous probe
+// With fix: This probe matches - exponential backoff continues (1.600s after previous probe)
++0.600~+0.700 > . 5:5(0) ack 1
--
2.43.7
On Mon, Oct 27, 2025 at 10:15 AM HaiYang Zhong <wokezhong@gmail.com> wrote: > > Move the packetdrill test to the packetdrill directory and shorten > the test duration. > > In the previous packetdrill test script, the long duration was due to > presenting the entire zero-window probe backoff process. The test has > been modified to only observe the first few packets to shorten the test > time while still effectively verifying the fix. > > - Moved test to tools/testing/selftests/net/packetdrill/ > - Reduced test duration from 360+ seconds to under 4 seconds > > Signed-off-by: HaiYang Zhong <wokezhong@tencent.com> > --- > .../packetdrill/tcp_fin_wait1_zero_window.pkt | 34 +++++++++++++++++++ > 1 file changed, 34 insertions(+) > create mode 100644 tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt > > diff --git a/tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt b/tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt > new file mode 100644 > index 000000000000..854ede56e7dd > --- /dev/null > +++ b/tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt > @@ -0,0 +1,34 @@ > +// Test for permanent FIN-WAIT-1 state with continuous zero-window advertisements > +// Author: HaiYang Zhong <wokezhong@tencent.com> > + > + > +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 <mss 1460> > +0.100 > S. 0:0(0) ack 1 <mss 1460> > +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.000 < . 1:1(0) ack 6 win 0 > + > +// Without fix: This probe won't match - timer was reset, probe will be sent 2.600s after the previous probe > +// With fix: This probe matches - exponential backoff continues (1.600s after previous probe) > ++0.600~+0.700 > . 5:5(0) ack 1 > -- Thanks for this test! Kuniyuki rightly raised a concern about the test execution time. But IMHO it was very nice that the original version of the test verified that the connection would eventually be timed out. With this shorter version of the test, AFAICT the test does not verify that the connection actually times out eventually. Perhaps if we tune the timeout settings we can achieve both (a) fast execution (say, less than 10 secs?), and (b) verify that the connection does time out? Perhaps you can try: + setting net.ipv4.tcp_orphan_retries to something small, like 3 or 4 (instead of the default of 0, which dynamically sets the retry count to 8 in tcp_orphan_retries()) + setting net.ipv4.tcp_rto_max_ms to something small, like 5000 (instead of the default of 120000, aka 120 secs) Another thought: the original test injected a lot of extra rwin=0 ACKs that AFAICT a real remote peer would not have sent. IMHO it's better to keep the test simpler and more realistic by not having the test inject those extra rwin=0 ACKs. Thanks, neal
On Mon, Oct 27, 2025 at 8:08 AM Neal Cardwell <ncardwell@google.com> wrote: > > On Mon, Oct 27, 2025 at 10:15 AM HaiYang Zhong <wokezhong@gmail.com> wrote: > > > > Move the packetdrill test to the packetdrill directory and shorten > > the test duration. > > > > In the previous packetdrill test script, the long duration was due to > > presenting the entire zero-window probe backoff process. The test has > > been modified to only observe the first few packets to shorten the test > > time while still effectively verifying the fix. > > > > - Moved test to tools/testing/selftests/net/packetdrill/ > > - Reduced test duration from 360+ seconds to under 4 seconds > > > > Signed-off-by: HaiYang Zhong <wokezhong@tencent.com> > > --- > > .../packetdrill/tcp_fin_wait1_zero_window.pkt | 34 +++++++++++++++++++ > > 1 file changed, 34 insertions(+) > > create mode 100644 tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt > > > > diff --git a/tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt b/tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt > > new file mode 100644 > > index 000000000000..854ede56e7dd > > --- /dev/null > > +++ b/tools/testing/selftests/net/packetdrill/tcp_fin_wait1_zero_window.pkt > > @@ -0,0 +1,34 @@ > > +// Test for permanent FIN-WAIT-1 state with continuous zero-window advertisements > > +// Author: HaiYang Zhong <wokezhong@tencent.com> > > + > > + > > +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 <mss 1460> > > +0.100 > S. 0:0(0) ack 1 <mss 1460> > > +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.000 < . 1:1(0) ack 6 win 0 > > + > > +// Without fix: This probe won't match - timer was reset, probe will be sent 2.600s after the previous probe > > +// With fix: This probe matches - exponential backoff continues (1.600s after previous probe) > > ++0.600~+0.700 > . 5:5(0) ack 1 > > -- > > Thanks for this test! > > Kuniyuki rightly raised a concern about the test execution time. > > But IMHO it was very nice that the original version of the test > verified that the connection would eventually be timed out. With this > shorter version of the test, AFAICT the test does not verify that the > connection actually times out eventually. > > Perhaps if we tune the timeout settings we can achieve both (a) fast > execution (say, less than 10 secs?), and (b) verify that the > connection does time out? +1 for the original test with shorter timeout settings. If the test still takes longer than 45s with the Neal's recommendation applied, we can adjust the kselftest timeout like : diff --git a/tools/testing/selftests/net/packetdrill/Makefile b/tools/testing/selftests/net/packetdrill/Makefile index ff54641493e9..33e3311e3ef5 100644 --- a/tools/testing/selftests/net/packetdrill/Makefile +++ b/tools/testing/selftests/net/packetdrill/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 +export kselftest_override_timeout=360 + TEST_INCLUDES := \ defaults.sh \ ksft_runner.sh \ Also, please post the next version as a separate thread so that patchwork will not be confused. https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html#resending-after-review ---8<--- The new version of patches should be posted as a separate thread, not as a reply to the previous posting. Change log should include a link to the previous posting ---8<--- Thanks! > > Perhaps you can try: > > + setting net.ipv4.tcp_orphan_retries to something small, like 3 or 4 > (instead of the default of 0, which dynamically sets the retry count > to 8 in tcp_orphan_retries()) > > + setting net.ipv4.tcp_rto_max_ms to something small, like 5000 > (instead of the default of 120000, aka 120 secs) > > Another thought: the original test injected a lot of extra rwin=0 ACKs > that AFAICT a real remote peer would not have sent. IMHO it's better > to keep the test simpler and more realistic by not having the test > inject those extra rwin=0 ACKs.
© 2016 - 2026 Red Hat, Inc.