Skip to content

Commit 7ebc650

Browse files
committed
Merge branch 'tcp-symmetric-challenge-ack-for-seg-ack-snd-nxt'
Jiayuan Chen says: ==================== tcp: symmetric challenge ACK for SEG.ACK > SND.NXT Commit 354e4aa ("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. It also updates the existing tcp_ts_recent_invalid_ack.pkt selftest, which drives this exact path, to consume the new challenge ACK so bisect stays clean. Patch 2 adds a new packetdrill selftest that exercises RFC 5961 Section 5.2 on both edges of the acceptable window, filling a gap in the selftests tree (neither edge had dedicated coverage before). ==================== Link: https://patch.msgid.link/20260422123605.320000-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2 parents 4078c56 + cf94b3c commit 7ebc650

3 files changed

Lines changed: 58 additions & 4 deletions

File tree

net/ipv4/tcp_input.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4286,11 +4286,15 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
42864286
goto old_ack;
42874287
}
42884288

4289-
/* If the ack includes data we haven't sent yet, discard
4290-
* this segment (RFC793 Section 3.9).
4289+
/* If the ack includes data we haven't sent yet, drop the
4290+
* segment. RFC 793 Section 3.9 and RFC 5961 Section 5.2
4291+
* require us to send an ACK back in that case.
42914292
*/
4292-
if (after(ack, tp->snd_nxt))
4293+
if (after(ack, tp->snd_nxt)) {
4294+
if (!(flag & FLAG_NO_CHALLENGE_ACK))
4295+
tcp_send_challenge_ack(sk, false);
42934296
return -SKB_DROP_REASON_TCP_ACK_UNSENT_DATA;
4297+
}
42944298

42954299
if (after(ack, prior_snd_una)) {
42964300
flag |= FLAG_SND_UNA_ADVANCED;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
//
3+
// RFC 5961 Section 5.2 / RFC 793 Section 3.9: an incoming segment's
4+
// ACK value must lie in [SND.UNA - MAX.SND.WND, SND.NXT]; otherwise
5+
// the receiver MUST discard the segment and send a challenge ACK
6+
// back. Exercise both edges of that window in a single connection.
7+
8+
`./defaults.sh
9+
sysctl -q net.ipv4.tcp_invalid_ratelimit=0
10+
`
11+
12+
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
13+
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
14+
+0 bind(3, ..., ...) = 0
15+
+0 listen(3, 1) = 0
16+
17+
// Three-way handshake. Peer advertises rwnd = 1000 (no wscale), so
18+
// MAX.SND.WND is tracked as 1000.
19+
+0 < S 0:0(0) win 1000 <mss 1000,sackOK,nop,nop,nop,wscale 0>
20+
+0 > S. 0:0(0) ack 1 <...>
21+
+.1 < . 1:1(0) ack 1 win 1000
22+
+0 accept(3, ..., ...) = 4
23+
24+
// ---- Upper edge: SEG.ACK > SND.NXT --------------------------------
25+
// Server has sent nothing yet, so SND.UNA = SND.NXT = 1.
26+
// Peer sends a pure ACK with SEG.ACK = 2, beyond SND.NXT.
27+
+0 < . 1:1(0) ack 2 win 1000
28+
// Expect a challenge ACK: <SEQ = SND.NXT = 1, ACK = RCV.NXT = 1>.
29+
+0 > . 1:1(0) ack 1
30+
31+
// Advance SND.UNA past MAX.SND.WND so that the lower edge becomes
32+
// reachable. Issue two 1-MSS writes so each skb is exactly one MSS
33+
// and PSH is set by tcp_push() at the end of each sendmsg, keeping
34+
// the setup independent of the TSO / tcp_fragment split path.
35+
+0 write(4, ..., 1000) = 1000
36+
+0 > P. 1:1001(1000) ack 1
37+
+.01 < . 1:1(0) ack 1001 win 1000
38+
+0 write(4, ..., 1000) = 1000
39+
+0 > P. 1001:2001(1000) ack 1
40+
+.01 < . 1:1(0) ack 2001 win 1000
41+
// Now SND.UNA = SND.NXT = 2001, MAX.SND.WND = 1000, bytes_acked = 2000.
42+
43+
// ---- Lower edge: SEG.ACK < SND.UNA - MAX.SND.WND ------------------
44+
// SND.UNA - MAX.SND.WND = 2001 - 1000 = 1001, so SEG.ACK = 1000 falls
45+
// below the acceptable range.
46+
+0 < . 1:1(0) ack 1000 win 1000
47+
// Expect a challenge ACK: <SEQ = SND.NXT = 2001, ACK = RCV.NXT = 1>.
48+
+0 > . 2001:2001(0) ack 1

tools/testing/selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
// bad packet with high tsval (its ACK sequence is above our sndnxt)
2121
+0 < F. 1:1(0) ack 9999 win 20000 <nop,nop,TS val 200000 ecr 100>
22-
22+
// Challenge ACK for SEG.ACK > SND.NXT (RFC 5961 5.2 / RFC 793 3.9).
23+
// ecr=200 (not 200000) proves ts_recent was not updated from the bad packet.
24+
+0 > . 1:1(0) ack 1 <nop,nop,TS val 200 ecr 200>
2325

2426
+0 < . 1:1001(1000) ack 1 win 20000 <nop,nop,TS val 201 ecr 100>
2527
+0 > . 1:1(0) ack 1001 <nop,nop,TS val 200 ecr 201>

0 commit comments

Comments
 (0)