netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] ip_tunnel(ipv4): fix tunnels with "local any remote $remote_ip"
@ 2014-07-28 23:07 Dmitry Popov
  2014-07-28 23:39 ` Cong Wang
  0 siblings, 1 reply; 5+ messages in thread
From: Dmitry Popov @ 2014-07-28 23:07 UTC (permalink / raw)
  To: David S. Miller, netdev; +Cc: Sergey Popov, Eric Dumazet, Tom Herbert

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>
---
 include/net/ip_tunnels.h |  1 +
 net/ipv4/ip_tunnel.c     | 29 ++++++++++++++++++-----------
 2 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index a4daf9e..8dd8cab 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -40,6 +40,7 @@ struct ip_tunnel_prl_entry {
 
 struct ip_tunnel_dst {
 	struct dst_entry __rcu 		*dst;
+	__be32				 saddr;
 };
 
 struct ip_tunnel {
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 6f9de61..45920d9 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)
 }
 
 static void __tunnel_dst_set(struct ip_tunnel_dst *idst,
-			     struct dst_entry *dst)
+			     struct dst_entry *dst, __be32 saddr)
 {
 	struct dst_entry *old_dst;
 
 	dst_clone(dst);
 	old_dst = xchg((__force struct dst_entry **)&idst->dst, dst);
 	dst_release(old_dst);
+	idst->saddr = saddr;
 }
 
-static void tunnel_dst_set(struct ip_tunnel *t, struct dst_entry *dst)
+static void tunnel_dst_set(struct ip_tunnel *t,
+			   struct dst_entry *dst, __be32 saddr)
 {
-	__tunnel_dst_set(this_cpu_ptr(t->dst_cache), dst);
+	__tunnel_dst_set(this_cpu_ptr(t->dst_cache), dst, saddr);
 }
 
 static void tunnel_dst_reset(struct ip_tunnel *t)
 {
-	tunnel_dst_set(t, NULL);
+	tunnel_dst_set(t, NULL, 0);
 }
 
 void ip_tunnel_dst_reset_all(struct ip_tunnel *t)
@@ -93,20 +95,25 @@ void ip_tunnel_dst_reset_all(struct ip_tunnel *t)
 	int i;
 
 	for_each_possible_cpu(i)
-		__tunnel_dst_set(per_cpu_ptr(t->dst_cache, i), NULL);
+		__tunnel_dst_set(per_cpu_ptr(t->dst_cache, i), NULL, 0);
 }
 EXPORT_SYMBOL(ip_tunnel_dst_reset_all);
 
-static struct rtable *tunnel_rtable_get(struct ip_tunnel *t, u32 cookie)
+static struct rtable *tunnel_rtable_get(struct ip_tunnel *t,
+					u32 cookie, __be32 *saddr)
 {
+	struct ip_tunnel_dst *idst;
 	struct dst_entry *dst;
 
 	rcu_read_lock();
-	dst = rcu_dereference(this_cpu_ptr(t->dst_cache)->dst);
+	idst = this_cpu_ptr(t->dst_cache);
+	dst = rcu_dereference(idst->dst);
 	if (dst && !atomic_inc_not_zero(&dst->__refcnt))
 		dst = NULL;
 	if (dst) {
-		if (dst->obsolete && dst->ops->check(dst, cookie) == NULL) {
+		if (!dst->obsolete || dst->ops->check(dst, cookie)) {
+			*saddr = idst->saddr;
+		} else {
 			tunnel_dst_reset(t);
 			dst_release(dst);
 			dst = NULL;
@@ -367,7 +374,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev)
 
 		if (!IS_ERR(rt)) {
 			tdev = rt->dst.dev;
-			tunnel_dst_set(tunnel, &rt->dst);
+			tunnel_dst_set(tunnel, &rt->dst, fl4.saddr);
 			ip_rt_put(rt);
 		}
 		if (dev->type != ARPHRD_ETHER)
@@ -610,7 +617,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
 	init_tunnel_flow(&fl4, protocol, dst, tnl_params->saddr,
 			 tunnel->parms.o_key, RT_TOS(tos), tunnel->parms.link);
 
-	rt = connected ? tunnel_rtable_get(tunnel, 0) : NULL;
+	rt = connected ? tunnel_rtable_get(tunnel, 0, &fl4.saddr) : NULL;
 
 	if (!rt) {
 		rt = ip_route_output_key(tunnel->net, &fl4);
@@ -620,7 +627,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
 			goto tx_error;
 		}
 		if (connected)
-			tunnel_dst_set(tunnel, &rt->dst);
+			tunnel_dst_set(tunnel, &rt->dst, fl4.saddr);
 	}
 
 	if (rt->dst.dev == dev) {

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] ip_tunnel(ipv4): fix tunnels with "local any remote $remote_ip"
  2014-07-28 23:07 [PATCH] ip_tunnel(ipv4): fix tunnels with "local any remote $remote_ip" Dmitry Popov
@ 2014-07-28 23:39 ` Cong Wang
  2014-07-28 23:44   ` David Miller
  2014-07-29 17:49   ` Dmitry Popov
  0 siblings, 2 replies; 5+ messages in thread
From: Cong Wang @ 2014-07-28 23:39 UTC (permalink / raw)
  To: Dmitry Popov
  Cc: David S. Miller, netdev, Sergey Popov, Eric Dumazet, Tom Herbert

On Mon, Jul 28, 2014 at 4:07 PM, Dmitry Popov <ixaphire@qrator.net> wrote:
> --- a/include/net/ip_tunnels.h
> +++ b/include/net/ip_tunnels.h
> @@ -40,6 +40,7 @@ struct ip_tunnel_prl_entry {
>
>  struct ip_tunnel_dst {
>         struct dst_entry __rcu          *dst;
> +       __be32                           saddr;
>  };


It looks odd to save a source address in some struct named dst,
maybe it's the time to rename it.


>         if (dst) {
> -               if (dst->obsolete && dst->ops->check(dst, cookie) == NULL) {
> +               if (!dst->obsolete || dst->ops->check(dst, cookie)) {
> +                       *saddr = idst->saddr;
> +               } else {

I am wondering if this always works, that is, if saddr could be still valid
as long as dst is valid?

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] ip_tunnel(ipv4): fix tunnels with "local any remote $remote_ip"
  2014-07-28 23:39 ` Cong Wang
@ 2014-07-28 23:44   ` David Miller
  2014-07-29 17:49   ` Dmitry Popov
  1 sibling, 0 replies; 5+ messages in thread
From: David Miller @ 2014-07-28 23:44 UTC (permalink / raw)
  To: cwang; +Cc: ixaphire, netdev, pinkbyte, edumazet, therbert

From: Cong Wang <cwang@twopensource.com>
Date: Mon, 28 Jul 2014 16:39:23 -0700

> On Mon, Jul 28, 2014 at 4:07 PM, Dmitry Popov <ixaphire@qrator.net> wrote:
>> --- a/include/net/ip_tunnels.h
>> +++ b/include/net/ip_tunnels.h
>> @@ -40,6 +40,7 @@ struct ip_tunnel_prl_entry {
>>
>>  struct ip_tunnel_dst {
>>         struct dst_entry __rcu          *dst;
>> +       __be32                           saddr;
>>  };
> 
> 
> It looks odd to save a source address in some struct named dst,
> maybe it's the time to rename it.

It's an encapsulation struct for plain "struct dst_entry", which is a
path to a destination, or in other words a "route".  A "saddr" is a
valid key for a route.

I see no reason to change the name of this structure, it's perfect.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] ip_tunnel(ipv4): fix tunnels with "local any remote $remote_ip"
  2014-07-28 23:39 ` Cong Wang
  2014-07-28 23:44   ` David Miller
@ 2014-07-29 17:49   ` Dmitry Popov
  2014-07-31  0:16     ` David Miller
  1 sibling, 1 reply; 5+ messages in thread
From: Dmitry Popov @ 2014-07-29 17:49 UTC (permalink / raw)
  To: Cong Wang
  Cc: David S. Miller, netdev, Sergey Popov, Eric Dumazet, Tom Herbert

On Mon, 28 Jul 2014 16:39:23 -0700
Cong Wang <cwang@twopensource.com> wrote:

> On Mon, Jul 28, 2014 at 4:07 PM, Dmitry Popov <ixaphire@qrator.net> wrote:
> > --- a/include/net/ip_tunnels.h
> > +++ b/include/net/ip_tunnels.h
> > @@ -40,6 +40,7 @@ struct ip_tunnel_prl_entry {
> >
> >  struct ip_tunnel_dst {
> >         struct dst_entry __rcu          *dst;
> > +       __be32                           saddr;
> >  };
> 
> 
> It looks odd to save a source address in some struct named dst,
> maybe it's the time to rename it.

Yes, maybe a bit odd, but ip_tunnel_flow/ip_tunnel_route doesn't look much 
better for me, so I decided to keep it with old name. David is ok with this
name too.

> >         if (dst) {
> > -               if (dst->obsolete && dst->ops->check(dst, cookie) == NULL) {
> > +               if (!dst->obsolete || dst->ops->check(dst, cookie)) {
> > +                       *saddr = idst->saddr;
> > +               } else {
> 
> I am wondering if this always works, that is, if saddr could be still valid
> as long as dst is valid?

Right, this is not a very smooth place, but a) it should work with current ipv4
route implementation (it obsoletes all dst_entries on any route/addr change).
Yes, we shouldn't rely on implementation, but b) I don't know how (and why) this
implementation should be changed to break it. I think the assumption that
dst_entry is obsoleted when saddr is changed is pretty natural.
Please correct me if I'm wrong.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] ip_tunnel(ipv4): fix tunnels with "local any remote $remote_ip"
  2014-07-29 17:49   ` Dmitry Popov
@ 2014-07-31  0:16     ` David Miller
  0 siblings, 0 replies; 5+ messages in thread
From: David Miller @ 2014-07-31  0:16 UTC (permalink / raw)
  To: ixaphire; +Cc: cwang, netdev, pinkbyte, edumazet, therbert

From: Dmitry Popov <ixaphire@qrator.net>
Date: Tue, 29 Jul 2014 21:49:31 +0400

> On Mon, 28 Jul 2014 16:39:23 -0700
> Cong Wang <cwang@twopensource.com> wrote:
> 
>> On Mon, Jul 28, 2014 at 4:07 PM, Dmitry Popov <ixaphire@qrator.net> wrote:
>> >         if (dst) {
>> > -               if (dst->obsolete && dst->ops->check(dst, cookie) == NULL) {
>> > +               if (!dst->obsolete || dst->ops->check(dst, cookie)) {
>> > +                       *saddr = idst->saddr;
>> > +               } else {
>> 
>> I am wondering if this always works, that is, if saddr could be still valid
>> as long as dst is valid?
> 
> Right, this is not a very smooth place, but a) it should work with current ipv4
> route implementation (it obsoletes all dst_entries on any route/addr change).
> Yes, we shouldn't rely on implementation, but b) I don't know how (and why) this
> implementation should be changed to break it. I think the assumption that
> dst_entry is obsoleted when saddr is changed is pretty natural.
> Please correct me if I'm wrong.

Indeed, that is the current state of affairs and I can't see how we'd ever
change this in a way that would break this.

So patch applied, thank you.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2014-07-31  0:16 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-07-28 23:07 [PATCH] ip_tunnel(ipv4): fix tunnels with "local any remote $remote_ip" Dmitry Popov
2014-07-28 23:39 ` Cong Wang
2014-07-28 23:44   ` David Miller
2014-07-29 17:49   ` Dmitry Popov
2014-07-31  0:16     ` David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).