public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH net 0/2] tcp: symmetric challenge ACK for SEG.ACK > SND.NXT
@ 2026-04-20  2:54 Jiayuan Chen
  2026-04-20  2:54 ` [PATCH net 1/2] tcp: send a challenge ACK on " Jiayuan Chen
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Jiayuan Chen @ 2026-04-20  2:54 UTC (permalink / raw)
  To: netdev
  Cc: Jiayuan Chen, Eric Dumazet, Neal Cardwell, Kuniyuki Iwashima,
	David S. Miller, David Ahern, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Shuah Khan, linux-kernel, linux-kselftest

Commit 354e4aa391ed ("tcp: RFC 5961 5.2 Blind Data Injection Attack
Mitigation") quotes RFC 5961 Section 5.2 in full, which requires
that any incoming segment whose ACK value falls outside
[SND.UNA - MAX.SND.WND, SND.NXT] MUST be discarded and an ACK sent
back.  Linux currently sends that challenge ACK only on the lower
edge (SEG.ACK < SND.UNA - MAX.SND.WND); on the symmetric upper edge
(SEG.ACK > SND.NXT) the segment is silently dropped with
SKB_DROP_REASON_TCP_ACK_UNSENT_DATA.

Patch 1 completes the mitigation by emitting a rate-limited challenge
ACK on that branch, reusing tcp_send_challenge_ack() and honouring
FLAG_NO_CHALLENGE_ACK for consistency with the lower-edge case.

Patch 2 adds a packetdrill selftest under
tools/testing/selftests/net/packetdrill/ that verifies the new
behaviour.

Jiayuan Chen (2):
  tcp: send a challenge ACK on SEG.ACK > SND.NXT
  selftests/net: packetdrill: cover challenge ACK on SEG.ACK > SND.NXT

 net/ipv4/tcp_input.c                          | 10 ++++--
 .../tcp_rfc5961_ack-beyond-snd-nxt.pkt        | 31 +++++++++++++++++++
 2 files changed, 38 insertions(+), 3 deletions(-)
 create mode 100644 tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-beyond-snd-nxt.pkt

-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH net 1/2] tcp: send a challenge ACK on SEG.ACK > SND.NXT
  2026-04-20  2:54 [PATCH net 0/2] tcp: symmetric challenge ACK for SEG.ACK > SND.NXT Jiayuan Chen
@ 2026-04-20  2:54 ` Jiayuan Chen
  2026-04-20  7:21   ` Eric Dumazet
  2026-04-20  2:54 ` [PATCH net 2/2] selftests/net: packetdrill: cover " Jiayuan Chen
  2026-04-20 15:24 ` [PATCH net 0/2] tcp: symmetric challenge ACK for " Jakub Kicinski
  2 siblings, 1 reply; 7+ messages in thread
From: Jiayuan Chen @ 2026-04-20  2:54 UTC (permalink / raw)
  To: netdev
  Cc: Jiayuan Chen, Eric Dumazet, Neal Cardwell, Kuniyuki Iwashima,
	David S. Miller, David Ahern, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Shuah Khan, linux-kernel, linux-kselftest

RFC 5961 Section 5.2 validates an incoming segment's ACK value
against the range [SND.UNA - MAX.SND.WND, SND.NXT] and states:

  "All incoming segments whose ACK value doesn't satisfy the above
   condition MUST be discarded and an ACK sent back."

Commit 354e4aa391ed ("tcp: RFC 5961 5.2 Blind Data Injection Attack
Mitigation") opted Linux into this mitigation and implements the
challenge ACK on the lower side (SEG.ACK < SND.UNA - MAX.SND.WND),
but the symmetric upper side (SEG.ACK > SND.NXT) still takes the
pre-RFC-5961 path and silently returns
SKB_DROP_REASON_TCP_ACK_UNSENT_DATA, even though RFC 793 Section 3.9
(now RFC 9293 Section 3.10.7.4) has always required:

  "If the ACK acknowledges something not yet sent (SEG.ACK > SND.NXT)
   then send an ACK, drop the segment, and return."

Complete the mitigation by sending a challenge ACK on that branch,
reusing the existing tcp_send_challenge_ack() path which already
enforces the per-socket RFC 5961 Section 7 rate limit via
__tcp_oow_rate_limited().  FLAG_NO_CHALLENGE_ACK is honoured for
symmetry with the lower-edge case.

Fixes: 354e4aa391ed ("tcp: RFC 5961 5.2 Blind Data Injection Attack Mitigation")
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>

---
I'm not sure if 'blamed commit' is appropriate, because I think
it's due to missing parts of the implementation, or it might be
directly targeted to net-next.
---
 net/ipv4/tcp_input.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 021f745747c5..c2b6f05acdfa 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -4284,11 +4284,15 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
 		goto old_ack;
 	}
 
-	/* If the ack includes data we haven't sent yet, discard
-	 * this segment (RFC793 Section 3.9).
+	/* If the ack includes data we haven't sent yet, drop the
+	 * segment.  RFC 793 Section 3.9 and RFC 5961 Section 5.2
+	 * require us to send an ACK back in that case.
 	 */
-	if (after(ack, tp->snd_nxt))
+	if (after(ack, tp->snd_nxt)) {
+		if (!(flag & FLAG_NO_CHALLENGE_ACK))
+			tcp_send_challenge_ack(sk, false);
 		return -SKB_DROP_REASON_TCP_ACK_UNSENT_DATA;
+	}
 
 	if (after(ack, prior_snd_una)) {
 		flag |= FLAG_SND_UNA_ADVANCED;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH net 2/2] selftests/net: packetdrill: cover challenge ACK on SEG.ACK > SND.NXT
  2026-04-20  2:54 [PATCH net 0/2] tcp: symmetric challenge ACK for SEG.ACK > SND.NXT Jiayuan Chen
  2026-04-20  2:54 ` [PATCH net 1/2] tcp: send a challenge ACK on " Jiayuan Chen
@ 2026-04-20  2:54 ` Jiayuan Chen
  2026-04-20  7:22   ` Eric Dumazet
  2026-04-20 15:24 ` [PATCH net 0/2] tcp: symmetric challenge ACK for " Jakub Kicinski
  2 siblings, 1 reply; 7+ messages in thread
From: Jiayuan Chen @ 2026-04-20  2:54 UTC (permalink / raw)
  To: netdev
  Cc: Jiayuan Chen, Eric Dumazet, Neal Cardwell, Kuniyuki Iwashima,
	David S. Miller, David Ahern, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Shuah Khan, linux-kernel, linux-kselftest

Exercise the RFC 5961 Section 5.2 / RFC 793 Section 3.9 requirement
on the upper edge of the acceptable ACK range, mirroring the existing
coverage of the SEG.ACK < SND.UNA - MAX.SND.WND case.

After the peer ACKs data the receiver has never sent, the receiver
must respond with <SEQ = SND.NXT, ACK = RCV.NXT, CTL = ACK> and drop
the offending segment.  The script validates this exact response.

Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
---
 .../tcp_rfc5961_ack-beyond-snd-nxt.pkt        | 31 +++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-beyond-snd-nxt.pkt

diff --git a/tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-beyond-snd-nxt.pkt b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-beyond-snd-nxt.pkt
new file mode 100644
index 000000000000..29d61a98d5ef
--- /dev/null
+++ b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-beyond-snd-nxt.pkt
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// RFC 5961 Section 5.2 / RFC 793 Section 3.9: when a received
+// segment's SEG.ACK acknowledges data we have never sent
+// (SEG.ACK > SND.NXT), the receiver MUST discard the segment and
+// send a challenge ACK back.
+//
+// Complement the existing mitigation for the symmetric
+// (SEG.ACK < SND.UNA - MAX.SND.WND) case.
+
+`./defaults.sh`
+
+   // Open listener socket
+    0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+   +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+   +0 bind(3, ..., ...) = 0
+   +0 listen(3, 1) = 0
+
+   // Three-way handshake
+   +0 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+   +0 > S. 0:0(0) ack 1 <...>
+ +.1 < . 1:1(0) ack 1 win 257
+   +0 accept(3, ..., ...) = 4
+
+   // Server has sent no data yet: SND.UNA = SND.NXT = 1, RCV.NXT = 1.
+   // Peer sends a pure ACK with SEG.ACK = 2, which is > SND.NXT.
+   +0 < . 1:1(0) ack 2 win 257
+
+   // Expect an immediate challenge ACK:
+   //   <SEQ = SND.NXT = 1, ACK = RCV.NXT = 1, CTL = ACK>
+   +0 > . 1:1(0) ack 1
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH net 1/2] tcp: send a challenge ACK on SEG.ACK > SND.NXT
  2026-04-20  2:54 ` [PATCH net 1/2] tcp: send a challenge ACK on " Jiayuan Chen
@ 2026-04-20  7:21   ` Eric Dumazet
  0 siblings, 0 replies; 7+ messages in thread
From: Eric Dumazet @ 2026-04-20  7:21 UTC (permalink / raw)
  To: Jiayuan Chen
  Cc: netdev, Neal Cardwell, Kuniyuki Iwashima, David S. Miller,
	David Ahern, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Shuah Khan, linux-kernel, linux-kselftest

On Sun, Apr 19, 2026 at 7:55 PM Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
>
> RFC 5961 Section 5.2 validates an incoming segment's ACK value
> against the range [SND.UNA - MAX.SND.WND, SND.NXT] and states:
>
>   "All incoming segments whose ACK value doesn't satisfy the above
>    condition MUST be discarded and an ACK sent back."
>
> Commit 354e4aa391ed ("tcp: RFC 5961 5.2 Blind Data Injection Attack
> Mitigation") opted Linux into this mitigation and implements the
> challenge ACK on the lower side (SEG.ACK < SND.UNA - MAX.SND.WND),
> but the symmetric upper side (SEG.ACK > SND.NXT) still takes the
> pre-RFC-5961 path and silently returns
> SKB_DROP_REASON_TCP_ACK_UNSENT_DATA, even though RFC 793 Section 3.9
> (now RFC 9293 Section 3.10.7.4) has always required:
>
>   "If the ACK acknowledges something not yet sent (SEG.ACK > SND.NXT)
>    then send an ACK, drop the segment, and return."
>
> Complete the mitigation by sending a challenge ACK on that branch,
> reusing the existing tcp_send_challenge_ack() path which already
> enforces the per-socket RFC 5961 Section 7 rate limit via
> __tcp_oow_rate_limited().  FLAG_NO_CHALLENGE_ACK is honoured for
> symmetry with the lower-edge case.
>
> Fixes: 354e4aa391ed ("tcp: RFC 5961 5.2 Blind Data Injection Attack Mitigation")
> Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
>
> ---
> I'm not sure if 'blamed commit' is appropriate, because I think
> it's due to missing parts of the implementation, or it might be
> directly targeted to net-next.

The Fixes: tag seems appropriate, and net tree LGTM.

Reviewed-by: Eric Dumazet <edumazet@google.com>

Thanks!

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net 2/2] selftests/net: packetdrill: cover challenge ACK on SEG.ACK > SND.NXT
  2026-04-20  2:54 ` [PATCH net 2/2] selftests/net: packetdrill: cover " Jiayuan Chen
@ 2026-04-20  7:22   ` Eric Dumazet
  0 siblings, 0 replies; 7+ messages in thread
From: Eric Dumazet @ 2026-04-20  7:22 UTC (permalink / raw)
  To: Jiayuan Chen
  Cc: netdev, Neal Cardwell, Kuniyuki Iwashima, David S. Miller,
	David Ahern, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Shuah Khan, linux-kernel, linux-kselftest

On Sun, Apr 19, 2026 at 7:55 PM Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
>
> Exercise the RFC 5961 Section 5.2 / RFC 793 Section 3.9 requirement
> on the upper edge of the acceptable ACK range, mirroring the existing
> coverage of the SEG.ACK < SND.UNA - MAX.SND.WND case.
>
> After the peer ACKs data the receiver has never sent, the receiver
> must respond with <SEQ = SND.NXT, ACK = RCV.NXT, CTL = ACK> and drop
> the offending segment.  The script validates this exact response.
>
> Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>

Reviewed-by: Eric Dumazet <edumazet@google.com>
Thanks!

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net 0/2] tcp: symmetric challenge ACK for SEG.ACK > SND.NXT
  2026-04-20  2:54 [PATCH net 0/2] tcp: symmetric challenge ACK for SEG.ACK > SND.NXT Jiayuan Chen
  2026-04-20  2:54 ` [PATCH net 1/2] tcp: send a challenge ACK on " Jiayuan Chen
  2026-04-20  2:54 ` [PATCH net 2/2] selftests/net: packetdrill: cover " Jiayuan Chen
@ 2026-04-20 15:24 ` Jakub Kicinski
  2026-04-20 15:38   ` Jiayuan Chen
  2 siblings, 1 reply; 7+ messages in thread
From: Jakub Kicinski @ 2026-04-20 15:24 UTC (permalink / raw)
  To: Jiayuan Chen
  Cc: netdev, Eric Dumazet, Neal Cardwell, Kuniyuki Iwashima,
	David S. Miller, David Ahern, Paolo Abeni, Simon Horman,
	Shuah Khan, linux-kernel, linux-kselftest

On Mon, 20 Apr 2026 10:54:07 +0800 Jiayuan Chen wrote:
> Commit 354e4aa391ed ("tcp: RFC 5961 5.2 Blind Data Injection Attack
> Mitigation") quotes RFC 5961 Section 5.2 in full, which requires
> that any incoming segment whose ACK value falls outside
> [SND.UNA - MAX.SND.WND, SND.NXT] MUST be discarded and an ACK sent
> back.  Linux currently sends that challenge ACK only on the lower
> edge (SEG.ACK < SND.UNA - MAX.SND.WND); on the symmetric upper edge
> (SEG.ACK > SND.NXT) the segment is silently dropped with
> SKB_DROP_REASON_TCP_ACK_UNSENT_DATA.
> 
> Patch 1 completes the mitigation by emitting a rate-limited challenge
> ACK on that branch, reusing tcp_send_challenge_ack() and honouring
> FLAG_NO_CHALLENGE_ACK for consistency with the lower-edge case.
> 
> Patch 2 adds a packetdrill selftest under
> tools/testing/selftests/net/packetdrill/ that verifies the new
> behaviour.

AI says:

Your patch "tcp: send a challenge ACK on SEG.ACK > SND.NXT" breaks an
existing packetdrill selftest:

  selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt

Test output:
  tcp_ts_recent_invalid_ack.pkt:25: error handling packet:
  live packet field tcp_ack_seq: expected: 1001 (0x3e9) vs actual: 1 (0x1)
  script packet:  0.200125 . 1:1(0) ack 1001 <nop,nop,TS val 200 ecr 201>
  actual packet:  0.200119 . 1:1(0) ack 1 win 65535 <nop,nop,TS val 200 ecr 200>
  not ok 1 ipv4
  not ok 2 ipv6
  not ok 3 ipv4-mapped-ipv6

Root cause:

The test `tcp_ts_recent_invalid_ack.pkt` sends a FIN+ACK with ACK=9999
(which exceeds SND.NXT=1) to verify that the kernel does not update ts_recent
from an invalid packet.  Before your patch, this packet was silently dropped.
After your patch, the kernel now emits a challenge ACK (SEQ=1, ACK=1) in
response to the ACK=9999 segment.

The test script does not expect this challenge ACK, so when it subsequently
tries to match the expected "ack 1001" response to the following data segment,
it instead sees the challenge ACK "ack 1", causing a mismatch on all three
address families (ipv4, ipv6, ipv4-mapped-ipv6).

Fix: update `tcp_ts_recent_invalid_ack.pkt` to consume the new challenge ACK
before checking the response to the subsequent data segment.  For example,
add after the bad FIN+ACK line:

  +0 > . 1:1(0) ack 1

so that the challenge ACK is explicitly expected and the rest of the script
proceeds as before.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net 0/2] tcp: symmetric challenge ACK for SEG.ACK > SND.NXT
  2026-04-20 15:24 ` [PATCH net 0/2] tcp: symmetric challenge ACK for " Jakub Kicinski
@ 2026-04-20 15:38   ` Jiayuan Chen
  0 siblings, 0 replies; 7+ messages in thread
From: Jiayuan Chen @ 2026-04-20 15:38 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, Eric Dumazet, Neal Cardwell, Kuniyuki Iwashima,
	David S. Miller, David Ahern, Paolo Abeni, Simon Horman,
	Shuah Khan, linux-kernel, linux-kselftest


On 4/20/26 11:24 PM, Jakub Kicinski wrote:
> AI says:
>
> Your patch "tcp: send a challenge ACK on SEG.ACK > SND.NXT" breaks an
> existing packetdrill selftest:
>
>    selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt
>
> Test output:
>    tcp_ts_recent_invalid_ack.pkt:25: error handling packet:
>    live packet field tcp_ack_seq: expected: 1001 (0x3e9) vs actual: 1 (0x1)
>    script packet:  0.200125 . 1:1(0) ack 1001 <nop,nop,TS val 200 ecr 201>
>    actual packet:  0.200119 . 1:1(0) ack 1 win 65535 <nop,nop,TS val 200 ecr 200>
>    not ok 1 ipv4
>    not ok 2 ipv6
>    not ok 3 ipv4-mapped-ipv6
>
> Root cause:
>
> The test `tcp_ts_recent_invalid_ack.pkt` sends a FIN+ACK with ACK=9999
> (which exceeds SND.NXT=1) to verify that the kernel does not update ts_recent
> from an invalid packet.  Before your patch, this packet was silently dropped.
> After your patch, the kernel now emits a challenge ACK (SEQ=1, ACK=1) in
> response to the ACK=9999 segment.
>
> The test script does not expect this challenge ACK, so when it subsequently
> tries to match the expected "ack 1001" response to the following data segment,
> it instead sees the challenge ACK "ack 1", causing a mismatch on all three
> address families (ipv4, ipv6, ipv4-mapped-ipv6).
>
> Fix: update `tcp_ts_recent_invalid_ack.pkt` to consume the new challenge ACK
> before checking the response to the subsequent data segment.  For example,
> add after the bad FIN+ACK line:
>
>    +0 > . 1:1(0) ack 1
>
> so that the challenge ACK is explicitly expected and the rest of the script
> proceeds as before.


Thanks Jakub, you're right.

Will fold the tcp_ts_recent_invalid_ack.pkt update into the kernel fix in v2

to keep bisect clean and makes the backport self-contained.


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-04-20 15:38 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-20  2:54 [PATCH net 0/2] tcp: symmetric challenge ACK for SEG.ACK > SND.NXT Jiayuan Chen
2026-04-20  2:54 ` [PATCH net 1/2] tcp: send a challenge ACK on " Jiayuan Chen
2026-04-20  7:21   ` Eric Dumazet
2026-04-20  2:54 ` [PATCH net 2/2] selftests/net: packetdrill: cover " Jiayuan Chen
2026-04-20  7:22   ` Eric Dumazet
2026-04-20 15:24 ` [PATCH net 0/2] tcp: symmetric challenge ACK for " Jakub Kicinski
2026-04-20 15:38   ` Jiayuan Chen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox