| From a0331a06cc80b12ab98a4e66165622bb5ab537b0 Mon Sep 17 00:00:00 2001 |
| From: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> |
| Date: Fri, 12 Oct 2012 04:34:17 +0000 |
| Subject: tcp: resets are misrouted |
| |
| |
| From: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> |
| |
| [ Upstream commit 4c67525849e0b7f4bd4fab2487ec9e43ea52ef29 ] |
| |
| After commit e2446eaa ("tcp_v4_send_reset: binding oif to iif in no |
| sock case").. tcp resets are always lost, when routing is asymmetric. |
| Yes, backing out that patch will result in misrouting of resets for |
| dead connections which used interface binding when were alive, but we |
| actually cannot do anything here. What's died that's died and correct |
| handling normal unbound connections is obviously a priority. |
| |
| Comment to comment: |
| > This has few benefits: |
| > 1. tcp_v6_send_reset already did that. |
| |
| It was done to route resets for IPv6 link local addresses. It was a |
| mistake to do so for global addresses. The patch fixes this as well. |
| |
| Actually, the problem appears to be even more serious than guaranteed |
| loss of resets. As reported by Sergey Soloviev <sol@eqv.ru>, those |
| misrouted resets create a lot of arp traffic and huge amount of |
| unresolved arp entires putting down to knees NAT firewalls which use |
| asymmetric routing. |
| |
| Signed-off-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv4/tcp_ipv4.c | 7 ++++--- |
| net/ipv6/tcp_ipv6.c | 3 ++- |
| 2 files changed, 6 insertions(+), 4 deletions(-) |
| |
| --- a/net/ipv4/tcp_ipv4.c |
| +++ b/net/ipv4/tcp_ipv4.c |
| @@ -678,10 +678,11 @@ static void tcp_v4_send_reset(struct soc |
| arg.csumoffset = offsetof(struct tcphdr, check) / 2; |
| arg.flags = (sk && inet_sk(sk)->transparent) ? IP_REPLY_ARG_NOSRCCHECK : 0; |
| /* When socket is gone, all binding information is lost. |
| - * routing might fail in this case. using iif for oif to |
| - * make sure we can deliver it |
| + * routing might fail in this case. No choice here, if we choose to force |
| + * input interface, we will misroute in case of asymmetric route. |
| */ |
| - arg.bound_dev_if = sk ? sk->sk_bound_dev_if : inet_iif(skb); |
| + if (sk) |
| + arg.bound_dev_if = sk->sk_bound_dev_if; |
| |
| net = dev_net(skb_dst(skb)->dev); |
| arg.tos = ip_hdr(skb)->tos; |
| --- a/net/ipv6/tcp_ipv6.c |
| +++ b/net/ipv6/tcp_ipv6.c |
| @@ -896,7 +896,8 @@ static void tcp_v6_send_response(struct |
| __tcp_v6_send_check(buff, &fl6.saddr, &fl6.daddr); |
| |
| fl6.flowi6_proto = IPPROTO_TCP; |
| - fl6.flowi6_oif = inet6_iif(skb); |
| + if (ipv6_addr_type(&fl6.daddr) & IPV6_ADDR_LINKLOCAL) |
| + fl6.flowi6_oif = inet6_iif(skb); |
| fl6.fl6_dport = t1->dest; |
| fl6.fl6_sport = t1->source; |
| security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); |