]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: ipv4: use a dedicated counter for icmp_v4 redirect packets
authorLorenzo Bianconi <lorenzo.bianconi@redhat.com>
Wed, 6 Feb 2019 18:18:04 +0000 (19:18 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 23 Feb 2019 08:04:25 +0000 (09:04 +0100)
[ Upstream commit c09551c6ff7fe16a79a42133bcecba5fc2fc3291 ]

According to the algorithm described in the comment block at the
beginning of ip_rt_send_redirect, the host should try to send
'ip_rt_redirect_number' ICMP redirect packets with an exponential
backoff and then stop sending them at all assuming that the destination
ignores redirects.
If the device has previously sent some ICMP error packets that are
rate-limited (e.g TTL expired) and continues to receive traffic,
the redirect packets will never be transmitted. This happens since
peer->rate_tokens will be typically greater than 'ip_rt_redirect_number'
and so it will never be reset even if the redirect silence timeout
(ip_rt_redirect_silence) has elapsed without receiving any packet
requiring redirects.

Fix it by using a dedicated counter for the number of ICMP redirect
packets that has been sent by the host

I have not been able to identify a given commit that introduced the
issue since ip_rt_send_redirect implements the same rate-limiting
algorithm from commit 1da177e4c3f4 ("Linux-2.6.12-rc2")

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/net/inetpeer.h
net/ipv4/inetpeer.c
net/ipv4/route.c

index 80479abddf73cc181899e1115a572727f578633d..43c946450b32359c426a7f21f0c8bd66c759c439 100644 (file)
@@ -35,6 +35,7 @@ struct inet_peer {
 
        u32                     metrics[RTAX_MAX];
        u32                     rate_tokens;    /* rate limiting for ICMP */
+       u32                     n_redirects;
        unsigned long           rate_last;
        union {
                struct list_head        gc_list;
index 241afd743d2ccfda93bfb42b928020f2d9dc0486..0571fd59a2644aa6b1ad0f144a8b27f28078ea38 100644 (file)
@@ -464,6 +464,7 @@ relookup:
                atomic_set(&p->rid, 0);
                p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW;
                p->rate_tokens = 0;
+               p->n_redirects = 0;
                /* 60*HZ is arbitrary, but chosen enough high so that the first
                 * calculation of tokens is at its maximum.
                 */
index b8a3a6c0b97ab08f2f894f5294b5cba64df811f9..ede610a4abc8ac89231f8c9819b60da3dfe1e2a9 100644 (file)
@@ -864,13 +864,15 @@ void ip_rt_send_redirect(struct sk_buff *skb)
        /* No redirected packets during ip_rt_redirect_silence;
         * reset the algorithm.
         */
-       if (time_after(jiffies, peer->rate_last + ip_rt_redirect_silence))
+       if (time_after(jiffies, peer->rate_last + ip_rt_redirect_silence)) {
                peer->rate_tokens = 0;
+               peer->n_redirects = 0;
+       }
 
        /* Too many ignored redirects; do not send anything
         * set dst.rate_last to the last seen redirected packet.
         */
-       if (peer->rate_tokens >= ip_rt_redirect_number) {
+       if (peer->n_redirects >= ip_rt_redirect_number) {
                peer->rate_last = jiffies;
                goto out_put_peer;
        }
@@ -887,6 +889,7 @@ void ip_rt_send_redirect(struct sk_buff *skb)
                icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, gw);
                peer->rate_last = jiffies;
                ++peer->rate_tokens;
+               ++peer->n_redirects;
 #ifdef CONFIG_IP_ROUTE_VERBOSE
                if (log_martians &&
                    peer->rate_tokens == ip_rt_redirect_number)