aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/tcp_input.c')
-rw-r--r--net/ipv4/tcp_input.c54
1 files changed, 32 insertions, 22 deletions
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 94d4aff97523..f9f9e375d7de 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -87,7 +87,7 @@ int sysctl_tcp_adv_win_scale __read_mostly = 1;
87EXPORT_SYMBOL(sysctl_tcp_adv_win_scale); 87EXPORT_SYMBOL(sysctl_tcp_adv_win_scale);
88 88
89/* rfc5961 challenge ack rate limiting */ 89/* rfc5961 challenge ack rate limiting */
90int sysctl_tcp_challenge_ack_limit = 100; 90int sysctl_tcp_challenge_ack_limit = 1000;
91 91
92int sysctl_tcp_stdurg __read_mostly; 92int sysctl_tcp_stdurg __read_mostly;
93int sysctl_tcp_rfc1337 __read_mostly; 93int sysctl_tcp_rfc1337 __read_mostly;
@@ -3424,6 +3424,23 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32
3424 return flag; 3424 return flag;
3425} 3425}
3426 3426
3427static bool __tcp_oow_rate_limited(struct net *net, int mib_idx,
3428 u32 *last_oow_ack_time)
3429{
3430 if (*last_oow_ack_time) {
3431 s32 elapsed = (s32)(tcp_time_stamp - *last_oow_ack_time);
3432
3433 if (0 <= elapsed && elapsed < sysctl_tcp_invalid_ratelimit) {
3434 NET_INC_STATS(net, mib_idx);
3435 return true; /* rate-limited: don't send yet! */
3436 }
3437 }
3438
3439 *last_oow_ack_time = tcp_time_stamp;
3440
3441 return false; /* not rate-limited: go ahead, send dupack now! */
3442}
3443
3427/* Return true if we're currently rate-limiting out-of-window ACKs and 3444/* Return true if we're currently rate-limiting out-of-window ACKs and
3428 * thus shouldn't send a dupack right now. We rate-limit dupacks in 3445 * thus shouldn't send a dupack right now. We rate-limit dupacks in
3429 * response to out-of-window SYNs or ACKs to mitigate ACK loops or DoS 3446 * response to out-of-window SYNs or ACKs to mitigate ACK loops or DoS
@@ -3437,21 +3454,9 @@ bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb,
3437 /* Data packets without SYNs are not likely part of an ACK loop. */ 3454 /* Data packets without SYNs are not likely part of an ACK loop. */
3438 if ((TCP_SKB_CB(skb)->seq != TCP_SKB_CB(skb)->end_seq) && 3455 if ((TCP_SKB_CB(skb)->seq != TCP_SKB_CB(skb)->end_seq) &&
3439 !tcp_hdr(skb)->syn) 3456 !tcp_hdr(skb)->syn)
3440 goto not_rate_limited; 3457 return false;
3441
3442 if (*last_oow_ack_time) {
3443 s32 elapsed = (s32)(tcp_time_stamp - *last_oow_ack_time);
3444
3445 if (0 <= elapsed && elapsed < sysctl_tcp_invalid_ratelimit) {
3446 NET_INC_STATS(net, mib_idx);
3447 return true; /* rate-limited: don't send yet! */
3448 }
3449 }
3450
3451 *last_oow_ack_time = tcp_time_stamp;
3452 3458
3453not_rate_limited: 3459 return __tcp_oow_rate_limited(net, mib_idx, last_oow_ack_time);
3454 return false; /* not rate-limited: go ahead, send dupack now! */
3455} 3460}
3456 3461
3457/* RFC 5961 7 [ACK Throttling] */ 3462/* RFC 5961 7 [ACK Throttling] */
@@ -3461,21 +3466,26 @@ static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb)
3461 static u32 challenge_timestamp; 3466 static u32 challenge_timestamp;
3462 static unsigned int challenge_count; 3467 static unsigned int challenge_count;
3463 struct tcp_sock *tp = tcp_sk(sk); 3468 struct tcp_sock *tp = tcp_sk(sk);
3464 u32 now; 3469 u32 count, now;
3465 3470
3466 /* First check our per-socket dupack rate limit. */ 3471 /* First check our per-socket dupack rate limit. */
3467 if (tcp_oow_rate_limited(sock_net(sk), skb, 3472 if (__tcp_oow_rate_limited(sock_net(sk),
3468 LINUX_MIB_TCPACKSKIPPEDCHALLENGE, 3473 LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
3469 &tp->last_oow_ack_time)) 3474 &tp->last_oow_ack_time))
3470 return; 3475 return;
3471 3476
3472 /* Then check the check host-wide RFC 5961 rate limit. */ 3477 /* Then check host-wide RFC 5961 rate limit. */
3473 now = jiffies / HZ; 3478 now = jiffies / HZ;
3474 if (now != challenge_timestamp) { 3479 if (now != challenge_timestamp) {
3480 u32 half = (sysctl_tcp_challenge_ack_limit + 1) >> 1;
3481
3475 challenge_timestamp = now; 3482 challenge_timestamp = now;
3476 challenge_count = 0; 3483 WRITE_ONCE(challenge_count, half +
3484 prandom_u32_max(sysctl_tcp_challenge_ack_limit));
3477 } 3485 }
3478 if (++challenge_count <= sysctl_tcp_challenge_ack_limit) { 3486 count = READ_ONCE(challenge_count);
3487 if (count > 0) {
3488 WRITE_ONCE(challenge_count, count - 1);
3479 NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK); 3489 NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK);
3480 tcp_send_ack(sk); 3490 tcp_send_ack(sk);
3481 } 3491 }