Skip to content

Commit 95cb574

Browse files
Dmitry Popovdavem330
Dmitry Popov
authored andcommitted
ip_tunnel(ipv4): fix tunnels with "local any remote $remote_ip"
Ipv4 tunnels created with "local any remote $ip" didn't work properly since 7d442fa (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>
1 parent dabf24d commit 95cb574

File tree

2 files changed

+19
-11
lines changed

2 files changed

+19
-11
lines changed

include/net/ip_tunnels.h

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ struct ip_tunnel_prl_entry {
4040

4141
struct ip_tunnel_dst {
4242
struct dst_entry __rcu *dst;
43+
__be32 saddr;
4344
};
4445

4546
struct ip_tunnel {

net/ipv4/ip_tunnel.c

+18-11
Original file line numberDiff line numberDiff line change
@@ -69,44 +69,51 @@ static unsigned int ip_tunnel_hash(__be32 key, __be32 remote)
6969
}
7070

7171
static void __tunnel_dst_set(struct ip_tunnel_dst *idst,
72-
struct dst_entry *dst)
72+
struct dst_entry *dst, __be32 saddr)
7373
{
7474
struct dst_entry *old_dst;
7575

7676
dst_clone(dst);
7777
old_dst = xchg((__force struct dst_entry **)&idst->dst, dst);
7878
dst_release(old_dst);
79+
idst->saddr = saddr;
7980
}
8081

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)
8284
{
83-
__tunnel_dst_set(this_cpu_ptr(t->dst_cache), dst);
85+
__tunnel_dst_set(this_cpu_ptr(t->dst_cache), dst, saddr);
8486
}
8587

8688
static void tunnel_dst_reset(struct ip_tunnel *t)
8789
{
88-
tunnel_dst_set(t, NULL);
90+
tunnel_dst_set(t, NULL, 0);
8991
}
9092

9193
void ip_tunnel_dst_reset_all(struct ip_tunnel *t)
9294
{
9395
int i;
9496

9597
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);
9799
}
98100
EXPORT_SYMBOL(ip_tunnel_dst_reset_all);
99101

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)
101104
{
105+
struct ip_tunnel_dst *idst;
102106
struct dst_entry *dst;
103107

104108
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);
106111
if (dst && !atomic_inc_not_zero(&dst->__refcnt))
107112
dst = NULL;
108113
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 {
110117
tunnel_dst_reset(t);
111118
dst_release(dst);
112119
dst = NULL;
@@ -367,7 +374,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev)
367374

368375
if (!IS_ERR(rt)) {
369376
tdev = rt->dst.dev;
370-
tunnel_dst_set(tunnel, &rt->dst);
377+
tunnel_dst_set(tunnel, &rt->dst, fl4.saddr);
371378
ip_rt_put(rt);
372379
}
373380
if (dev->type != ARPHRD_ETHER)
@@ -610,7 +617,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
610617
init_tunnel_flow(&fl4, protocol, dst, tnl_params->saddr,
611618
tunnel->parms.o_key, RT_TOS(tos), tunnel->parms.link);
612619

613-
rt = connected ? tunnel_rtable_get(tunnel, 0) : NULL;
620+
rt = connected ? tunnel_rtable_get(tunnel, 0, &fl4.saddr) : NULL;
614621

615622
if (!rt) {
616623
rt = ip_route_output_key(tunnel->net, &fl4);
@@ -620,7 +627,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
620627
goto tx_error;
621628
}
622629
if (connected)
623-
tunnel_dst_set(tunnel, &rt->dst);
630+
tunnel_dst_set(tunnel, &rt->dst, fl4.saddr);
624631
}
625632

626633
if (rt->dst.dev == dev) {

0 commit comments

Comments
 (0)