diff options
author | Dmitry Popov <ixaphire@qrator.net> | 2014-07-28 19:07:52 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-07-30 18:18:58 -0400 |
commit | 95cb5745983c222867cc9ac593aebb2ad67d72c0 (patch) | |
tree | aea09d3b5173d5365eb42cdb2dd11eee831062c6 /net/ipv4 | |
parent | dabf24d168ae7da5c3dc8c383db64b200e9ab483 (diff) |
ip_tunnel(ipv4): fix tunnels with "local any remote $remote_ip"
Ipv4 tunnels created with "local any remote $ip" didn't work properly since
7d442fab0 (ipv4: Cache dst in tunnels). 99% of packets sent via those tunnels
had src addr = 0.0.0.0. That was because only dst_entry was cached, although
fl4.saddr has to be cached too. Every time ip_tunnel_xmit used cached dst_entry
(tunnel_rtable_get returned non-NULL), fl4.saddr was initialized with
tnl_params->saddr (= 0 in our case), and wasn't changed until iptunnel_xmit().
This patch adds saddr to ip_tunnel->dst_cache, fixing this issue.
Reported-by: Sergey Popov <pinkbyte@gentoo.org>
Signed-off-by: Dmitry Popov <ixaphire@qrator.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/ip_tunnel.c | 29 |
1 files changed, 18 insertions, 11 deletions
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 6f9de61dce5f..45920d928341 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c | |||
@@ -69,23 +69,25 @@ static unsigned int ip_tunnel_hash(__be32 key, __be32 remote) | |||
69 | } | 69 | } |
70 | 70 | ||
71 | static void __tunnel_dst_set(struct ip_tunnel_dst *idst, | 71 | static void __tunnel_dst_set(struct ip_tunnel_dst *idst, |
72 | struct dst_entry *dst) | 72 | struct dst_entry *dst, __be32 saddr) |
73 | { | 73 | { |
74 | struct dst_entry *old_dst; | 74 | struct dst_entry *old_dst; |
75 | 75 | ||
76 | dst_clone(dst); | 76 | dst_clone(dst); |
77 | old_dst = xchg((__force struct dst_entry **)&idst->dst, dst); | 77 | old_dst = xchg((__force struct dst_entry **)&idst->dst, dst); |
78 | dst_release(old_dst); | 78 | dst_release(old_dst); |
79 | idst->saddr = saddr; | ||
79 | } | 80 | } |
80 | 81 | ||
81 | static void tunnel_dst_set(struct ip_tunnel *t, struct dst_entry *dst) | 82 | static void tunnel_dst_set(struct ip_tunnel *t, |
83 | struct dst_entry *dst, __be32 saddr) | ||
82 | { | 84 | { |
83 | __tunnel_dst_set(this_cpu_ptr(t->dst_cache), dst); | 85 | __tunnel_dst_set(this_cpu_ptr(t->dst_cache), dst, saddr); |
84 | } | 86 | } |
85 | 87 | ||
86 | static void tunnel_dst_reset(struct ip_tunnel *t) | 88 | static void tunnel_dst_reset(struct ip_tunnel *t) |
87 | { | 89 | { |
88 | tunnel_dst_set(t, NULL); | 90 | tunnel_dst_set(t, NULL, 0); |
89 | } | 91 | } |
90 | 92 | ||
91 | void ip_tunnel_dst_reset_all(struct ip_tunnel *t) | 93 | void ip_tunnel_dst_reset_all(struct ip_tunnel *t) |
@@ -93,20 +95,25 @@ void ip_tunnel_dst_reset_all(struct ip_tunnel *t) | |||
93 | int i; | 95 | int i; |
94 | 96 | ||
95 | for_each_possible_cpu(i) | 97 | for_each_possible_cpu(i) |
96 | __tunnel_dst_set(per_cpu_ptr(t->dst_cache, i), NULL); | 98 | __tunnel_dst_set(per_cpu_ptr(t->dst_cache, i), NULL, 0); |
97 | } | 99 | } |
98 | EXPORT_SYMBOL(ip_tunnel_dst_reset_all); | 100 | EXPORT_SYMBOL(ip_tunnel_dst_reset_all); |
99 | 101 | ||
100 | static struct rtable *tunnel_rtable_get(struct ip_tunnel *t, u32 cookie) | 102 | static struct rtable *tunnel_rtable_get(struct ip_tunnel *t, |
103 | u32 cookie, __be32 *saddr) | ||
101 | { | 104 | { |
105 | struct ip_tunnel_dst *idst; | ||
102 | struct dst_entry *dst; | 106 | struct dst_entry *dst; |
103 | 107 | ||
104 | rcu_read_lock(); | 108 | rcu_read_lock(); |
105 | dst = rcu_dereference(this_cpu_ptr(t->dst_cache)->dst); | 109 | idst = this_cpu_ptr(t->dst_cache); |
110 | dst = rcu_dereference(idst->dst); | ||
106 | if (dst && !atomic_inc_not_zero(&dst->__refcnt)) | 111 | if (dst && !atomic_inc_not_zero(&dst->__refcnt)) |
107 | dst = NULL; | 112 | dst = NULL; |
108 | if (dst) { | 113 | if (dst) { |
109 | if (dst->obsolete && dst->ops->check(dst, cookie) == NULL) { | 114 | if (!dst->obsolete || dst->ops->check(dst, cookie)) { |
115 | *saddr = idst->saddr; | ||
116 | } else { | ||
110 | tunnel_dst_reset(t); | 117 | tunnel_dst_reset(t); |
111 | dst_release(dst); | 118 | dst_release(dst); |
112 | dst = NULL; | 119 | dst = NULL; |
@@ -367,7 +374,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev) | |||
367 | 374 | ||
368 | if (!IS_ERR(rt)) { | 375 | if (!IS_ERR(rt)) { |
369 | tdev = rt->dst.dev; | 376 | tdev = rt->dst.dev; |
370 | tunnel_dst_set(tunnel, &rt->dst); | 377 | tunnel_dst_set(tunnel, &rt->dst, fl4.saddr); |
371 | ip_rt_put(rt); | 378 | ip_rt_put(rt); |
372 | } | 379 | } |
373 | if (dev->type != ARPHRD_ETHER) | 380 | if (dev->type != ARPHRD_ETHER) |
@@ -610,7 +617,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, | |||
610 | init_tunnel_flow(&fl4, protocol, dst, tnl_params->saddr, | 617 | init_tunnel_flow(&fl4, protocol, dst, tnl_params->saddr, |
611 | tunnel->parms.o_key, RT_TOS(tos), tunnel->parms.link); | 618 | tunnel->parms.o_key, RT_TOS(tos), tunnel->parms.link); |
612 | 619 | ||
613 | rt = connected ? tunnel_rtable_get(tunnel, 0) : NULL; | 620 | rt = connected ? tunnel_rtable_get(tunnel, 0, &fl4.saddr) : NULL; |
614 | 621 | ||
615 | if (!rt) { | 622 | if (!rt) { |
616 | rt = ip_route_output_key(tunnel->net, &fl4); | 623 | rt = ip_route_output_key(tunnel->net, &fl4); |
@@ -620,7 +627,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, | |||
620 | goto tx_error; | 627 | goto tx_error; |
621 | } | 628 | } |
622 | if (connected) | 629 | if (connected) |
623 | tunnel_dst_set(tunnel, &rt->dst); | 630 | tunnel_dst_set(tunnel, &rt->dst, fl4.saddr); |
624 | } | 631 | } |
625 | 632 | ||
626 | if (rt->dst.dev == dev) { | 633 | if (rt->dst.dev == dev) { |