Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: ipv4: use a dedicated counter for icmp_v4 redirect packets

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>

authored by

Lorenzo Bianconi and committed by
David S. Miller
c09551c6 b5bfc21a

+7 -2
+1
include/net/inetpeer.h
··· 39 39 40 40 u32 metrics[RTAX_MAX]; 41 41 u32 rate_tokens; /* rate limiting for ICMP */ 42 + u32 n_redirects; 42 43 unsigned long rate_last; 43 44 /* 44 45 * Once inet_peer is queued for deletion (refcnt == 0), following field
+1
net/ipv4/inetpeer.c
··· 216 216 atomic_set(&p->rid, 0); 217 217 p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; 218 218 p->rate_tokens = 0; 219 + p->n_redirects = 0; 219 220 /* 60*HZ is arbitrary, but chosen enough high so that the first 220 221 * calculation of tokens is at its maximum. 221 222 */
+5 -2
net/ipv4/route.c
··· 887 887 /* No redirected packets during ip_rt_redirect_silence; 888 888 * reset the algorithm. 889 889 */ 890 - if (time_after(jiffies, peer->rate_last + ip_rt_redirect_silence)) 890 + if (time_after(jiffies, peer->rate_last + ip_rt_redirect_silence)) { 891 891 peer->rate_tokens = 0; 892 + peer->n_redirects = 0; 893 + } 892 894 893 895 /* Too many ignored redirects; do not send anything 894 896 * set dst.rate_last to the last seen redirected packet. 895 897 */ 896 - if (peer->rate_tokens >= ip_rt_redirect_number) { 898 + if (peer->n_redirects >= ip_rt_redirect_number) { 897 899 peer->rate_last = jiffies; 898 900 goto out_put_peer; 899 901 } ··· 912 910 icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, gw); 913 911 peer->rate_last = jiffies; 914 912 ++peer->rate_tokens; 913 + ++peer->n_redirects; 915 914 #ifdef CONFIG_IP_ROUTE_VERBOSE 916 915 if (log_martians && 917 916 peer->rate_tokens == ip_rt_redirect_number)