tcp: mitigate ACK loops for connections as tcp_sock
[cascardo/linux.git] / net / ipv4 / tcp_input.c
index 9401aa4..8fdd27b 100644 (file)
@@ -3322,13 +3322,22 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32
 }
 
 /* RFC 5961 7 [ACK Throttling] */
-static void tcp_send_challenge_ack(struct sock *sk)
+static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb)
 {
        /* unprotected vars, we dont care of overwrites */
        static u32 challenge_timestamp;
        static unsigned int challenge_count;
-       u32 now = jiffies / HZ;
+       struct tcp_sock *tp = tcp_sk(sk);
+       u32 now;
+
+       /* First check our per-socket dupack rate limit. */
+       if (tcp_oow_rate_limited(sock_net(sk), skb,
+                                LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
+                                &tp->last_oow_ack_time))
+               return;
 
+       /* Then check the check host-wide RFC 5961 rate limit. */
+       now = jiffies / HZ;
        if (now != challenge_timestamp) {
                challenge_timestamp = now;
                challenge_count = 0;
@@ -3424,7 +3433,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
        if (before(ack, prior_snd_una)) {
                /* RFC 5961 5.2 [Blind Data Injection Attack].[Mitigation] */
                if (before(ack, prior_snd_una - tp->max_window)) {
-                       tcp_send_challenge_ack(sk);
+                       tcp_send_challenge_ack(sk, skb);
                        return -1;
                }
                goto old_ack;
@@ -4993,7 +5002,10 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
            tcp_paws_discard(sk, skb)) {
                if (!th->rst) {
                        NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
-                       tcp_send_dupack(sk, skb);
+                       if (!tcp_oow_rate_limited(sock_net(sk), skb,
+                                                 LINUX_MIB_TCPACKSKIPPEDPAWS,
+                                                 &tp->last_oow_ack_time))
+                               tcp_send_dupack(sk, skb);
                        goto discard;
                }
                /* 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,
                if (!th->rst) {
                        if (th->syn)
                                goto syn_challenge;
-                       tcp_send_dupack(sk, skb);
+                       if (!tcp_oow_rate_limited(sock_net(sk), skb,
+                                                 LINUX_MIB_TCPACKSKIPPEDSEQ,
+                                                 &tp->last_oow_ack_time))
+                               tcp_send_dupack(sk, skb);
                }
                goto discard;
        }
@@ -5026,7 +5041,7 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
                if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt)
                        tcp_reset(sk);
                else
-                       tcp_send_challenge_ack(sk);
+                       tcp_send_challenge_ack(sk, skb);
                goto discard;
        }
 
@@ -5040,7 +5055,7 @@ syn_challenge:
                if (syn_inerr)
                        TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNCHALLENGE);
-               tcp_send_challenge_ack(sk);
+               tcp_send_challenge_ack(sk, skb);
                goto discard;
        }