Skip to content

Commit f2b2c58

Browse files
nealcardwelldavem330
authored andcommitted
tcp: mitigate ACK loops for connections as tcp_sock
Ensure that in state ESTABLISHED, where the connection is represented by a tcp_sock, we rate limit dupacks in response to incoming packets (a) with TCP timestamps that fail PAWS checks, or (b) with sequence numbers or ACK numbers that are out of the acceptable window. We do not send a dupack in response to out-of-window packets if it has been less than sysctl_tcp_invalid_ratelimit (default 500ms) since we last sent a dupack in response to an out-of-window packet. There is already a similar (although global) rate-limiting mechanism for "challenge ACKs". When deciding whether to send a challence ACK, we first consult the new per-connection rate limit, and then the global rate limit. Reported-by: Avery Fay <[email protected]> Signed-off-by: Neal Cardwell <[email protected]> Signed-off-by: Yuchung Cheng <[email protected]> Signed-off-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent a9b2c06 commit f2b2c58

File tree

3 files changed

+24
-7
lines changed

3 files changed

+24
-7
lines changed

include/linux/tcp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ struct tcp_sock {
153153
u32 snd_sml; /* Last byte of the most recently transmitted small packet */
154154
u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) */
155155
u32 lsndtime; /* timestamp of last sent data packet (for restart window) */
156+
u32 last_oow_ack_time; /* timestamp of last out-of-window ACK */
156157

157158
u32 tsoffset; /* timestamp offset */
158159

net/ipv4/tcp_input.c

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3322,13 +3322,22 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32
33223322
}
33233323

33243324
/* RFC 5961 7 [ACK Throttling] */
3325-
static void tcp_send_challenge_ack(struct sock *sk)
3325+
static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb)
33263326
{
33273327
/* unprotected vars, we dont care of overwrites */
33283328
static u32 challenge_timestamp;
33293329
static unsigned int challenge_count;
3330-
u32 now = jiffies / HZ;
3330+
struct tcp_sock *tp = tcp_sk(sk);
3331+
u32 now;
3332+
3333+
/* First check our per-socket dupack rate limit. */
3334+
if (tcp_oow_rate_limited(sock_net(sk), skb,
3335+
LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
3336+
&tp->last_oow_ack_time))
3337+
return;
33313338

3339+
/* Then check the check host-wide RFC 5961 rate limit. */
3340+
now = jiffies / HZ;
33323341
if (now != challenge_timestamp) {
33333342
challenge_timestamp = now;
33343343
challenge_count = 0;
@@ -3424,7 +3433,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
34243433
if (before(ack, prior_snd_una)) {
34253434
/* RFC 5961 5.2 [Blind Data Injection Attack].[Mitigation] */
34263435
if (before(ack, prior_snd_una - tp->max_window)) {
3427-
tcp_send_challenge_ack(sk);
3436+
tcp_send_challenge_ack(sk, skb);
34283437
return -1;
34293438
}
34303439
goto old_ack;
@@ -4993,7 +5002,10 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
49935002
tcp_paws_discard(sk, skb)) {
49945003
if (!th->rst) {
49955004
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
4996-
tcp_send_dupack(sk, skb);
5005+
if (!tcp_oow_rate_limited(sock_net(sk), skb,
5006+
LINUX_MIB_TCPACKSKIPPEDPAWS,
5007+
&tp->last_oow_ack_time))
5008+
tcp_send_dupack(sk, skb);
49975009
goto discard;
49985010
}
49995011
/* Reset is accepted even if it did not pass PAWS. */
@@ -5010,7 +5022,10 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
50105022
if (!th->rst) {
50115023
if (th->syn)
50125024
goto syn_challenge;
5013-
tcp_send_dupack(sk, skb);
5025+
if (!tcp_oow_rate_limited(sock_net(sk), skb,
5026+
LINUX_MIB_TCPACKSKIPPEDSEQ,
5027+
&tp->last_oow_ack_time))
5028+
tcp_send_dupack(sk, skb);
50145029
}
50155030
goto discard;
50165031
}
@@ -5026,7 +5041,7 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
50265041
if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt)
50275042
tcp_reset(sk);
50285043
else
5029-
tcp_send_challenge_ack(sk);
5044+
tcp_send_challenge_ack(sk, skb);
50305045
goto discard;
50315046
}
50325047

@@ -5040,7 +5055,7 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
50405055
if (syn_inerr)
50415056
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
50425057
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNCHALLENGE);
5043-
tcp_send_challenge_ack(sk);
5058+
tcp_send_challenge_ack(sk, skb);
50445059
goto discard;
50455060
}
50465061

net/ipv4/tcp_minisocks.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
467467
tcp_enable_early_retrans(newtp);
468468
newtp->tlp_high_seq = 0;
469469
newtp->lsndtime = treq->snt_synack;
470+
newtp->last_oow_ack_time = 0;
470471
newtp->total_retrans = req->num_retrans;
471472

472473
/* So many TCP implementations out there (incorrectly) count the

0 commit comments

Comments
 (0)