From 12969569086d18bd6ee4235224ea098607e860b9 Mon Sep 17 00:00:00 2001
From: Steven Barth <cyrus@openwrt.org>
Date: Tue, 19 May 2015 07:43:59 +0000
Subject: [PATCH] linux: backport IPv6 SAS fixes for source-specific routes

Signed-off-by: Steven Barth <steven@midlink.org>

SVN-Revision: 45699
---
 ...urce-specific-default-route-handling.patch |  98 +++++++++++++++++
 ...urce-specific-default-route-handling.patch | 104 ++++++++++++++++++
 2 files changed, 202 insertions(+)
 create mode 100644 target/linux/generic/patches-3.18/667-ipv6-Fixed-source-specific-default-route-handling.patch
 create mode 100644 target/linux/generic/patches-4.0/667-ipv6-Fixed-source-specific-default-route-handling.patch

diff --git a/target/linux/generic/patches-3.18/667-ipv6-Fixed-source-specific-default-route-handling.patch b/target/linux/generic/patches-3.18/667-ipv6-Fixed-source-specific-default-route-handling.patch
new file mode 100644
index 0000000000..db93ee1614
--- /dev/null
+++ b/target/linux/generic/patches-3.18/667-ipv6-Fixed-source-specific-default-route-handling.patch
@@ -0,0 +1,98 @@
+From e16e888b525503be05b3aea64190e8b3bdef44d0 Mon Sep 17 00:00:00 2001
+From: Markus Stenberg <markus.stenberg@iki.fi>
+Date: Tue, 5 May 2015 13:36:59 +0300
+Subject: [PATCH] ipv6: Fixed source specific default route handling.
+
+If there are only IPv6 source specific default routes present, the
+host gets -ENETUNREACH on e.g. connect() because ip6_dst_lookup_tail
+calls ip6_route_output first, and given source address any, it fails,
+and ip6_route_get_saddr is never called.
+
+The change is to use the ip6_route_get_saddr, even if the initial
+ip6_route_output fails, and then doing ip6_route_output _again_ after
+we have appropriate source address available.
+
+Note that this is '99% fix' to the problem; a correct fix would be to
+do route lookups only within addrconf.c when picking a source address,
+and never call ip6_route_output before source address has been
+populated.
+
+Signed-off-by: Markus Stenberg <markus.stenberg@iki.fi>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/ipv6/ip6_output.c | 39 +++++++++++++++++++++++++++++++--------
+ net/ipv6/route.c      |  5 +++--
+ 2 files changed, 34 insertions(+), 10 deletions(-)
+
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 7fde1f2..c217775 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -897,21 +897,45 @@ static int ip6_dst_lookup_tail(struct so
+ #endif
+ 	int err;
+ 
+-	if (*dst == NULL)
+-		*dst = ip6_route_output(net, sk, fl6);
+-
+-	if ((err = (*dst)->error))
+-		goto out_err_release;
++	/* The correct way to handle this would be to do
++	 * ip6_route_get_saddr, and then ip6_route_output; however,
++	 * the route-specific preferred source forces the
++	 * ip6_route_output call _before_ ip6_route_get_saddr.
++	 *
++	 * In source specific routing (no src=any default route),
++	 * ip6_route_output will fail given src=any saddr, though, so
++	 * that's why we try it again later.
++	 */
++	if (ipv6_addr_any(&fl6->saddr) && (!*dst || !(*dst)->error)) {
++		struct rt6_info *rt;
++		bool had_dst = *dst != NULL;
+ 
+-	if (ipv6_addr_any(&fl6->saddr)) {
+-		struct rt6_info *rt = (struct rt6_info *) *dst;
++		if (!had_dst)
++			*dst = ip6_route_output(net, sk, fl6);
++		rt = (*dst)->error ? NULL : (struct rt6_info *)*dst;
+ 		err = ip6_route_get_saddr(net, rt, &fl6->daddr,
+ 					  sk ? inet6_sk(sk)->srcprefs : 0,
+ 					  &fl6->saddr);
+ 		if (err)
+ 			goto out_err_release;
++
++		/* If we had an erroneous initial result, pretend it
++		 * never existed and let the SA-enabled version take
++		 * over.
++		 */
++		if (!had_dst && (*dst)->error) {
++			dst_release(*dst);
++			*dst = NULL;
++		}
+ 	}
+ 
++	if (!*dst)
++		*dst = ip6_route_output(net, sk, fl6);
++
++	err = (*dst)->error;
++	if (err)
++		goto out_err_release;
++
+ #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+ 	/*
+ 	 * Here if the dst entry we've looked up
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -2215,9 +2215,10 @@ int ip6_route_get_saddr(struct net *net,
+ 			unsigned int prefs,
+ 			struct in6_addr *saddr)
+ {
+-	struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt);
++	struct inet6_dev *idev =
++		rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
+ 	int err = 0;
+-	if (rt->rt6i_prefsrc.plen)
++	if (rt && rt->rt6i_prefsrc.plen)
+ 		*saddr = rt->rt6i_prefsrc.addr;
+ 	else
+ 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
diff --git a/target/linux/generic/patches-4.0/667-ipv6-Fixed-source-specific-default-route-handling.patch b/target/linux/generic/patches-4.0/667-ipv6-Fixed-source-specific-default-route-handling.patch
new file mode 100644
index 0000000000..b5cc76dc48
--- /dev/null
+++ b/target/linux/generic/patches-4.0/667-ipv6-Fixed-source-specific-default-route-handling.patch
@@ -0,0 +1,104 @@
+From e16e888b525503be05b3aea64190e8b3bdef44d0 Mon Sep 17 00:00:00 2001
+From: Markus Stenberg <markus.stenberg@iki.fi>
+Date: Tue, 5 May 2015 13:36:59 +0300
+Subject: [PATCH] ipv6: Fixed source specific default route handling.
+
+If there are only IPv6 source specific default routes present, the
+host gets -ENETUNREACH on e.g. connect() because ip6_dst_lookup_tail
+calls ip6_route_output first, and given source address any, it fails,
+and ip6_route_get_saddr is never called.
+
+The change is to use the ip6_route_get_saddr, even if the initial
+ip6_route_output fails, and then doing ip6_route_output _again_ after
+we have appropriate source address available.
+
+Note that this is '99% fix' to the problem; a correct fix would be to
+do route lookups only within addrconf.c when picking a source address,
+and never call ip6_route_output before source address has been
+populated.
+
+Signed-off-by: Markus Stenberg <markus.stenberg@iki.fi>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/ipv6/ip6_output.c | 39 +++++++++++++++++++++++++++++++--------
+ net/ipv6/route.c      |  5 +++--
+ 2 files changed, 34 insertions(+), 10 deletions(-)
+
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 7fde1f2..c217775 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -886,22 +886,45 @@ static int ip6_dst_lookup_tail(struct sock *sk,
+ #endif
+ 	int err;
+ 
+-	if (!*dst)
+-		*dst = ip6_route_output(net, sk, fl6);
+-
+-	err = (*dst)->error;
+-	if (err)
+-		goto out_err_release;
++	/* The correct way to handle this would be to do
++	 * ip6_route_get_saddr, and then ip6_route_output; however,
++	 * the route-specific preferred source forces the
++	 * ip6_route_output call _before_ ip6_route_get_saddr.
++	 *
++	 * In source specific routing (no src=any default route),
++	 * ip6_route_output will fail given src=any saddr, though, so
++	 * that's why we try it again later.
++	 */
++	if (ipv6_addr_any(&fl6->saddr) && (!*dst || !(*dst)->error)) {
++		struct rt6_info *rt;
++		bool had_dst = *dst != NULL;
+ 
+-	if (ipv6_addr_any(&fl6->saddr)) {
+-		struct rt6_info *rt = (struct rt6_info *) *dst;
++		if (!had_dst)
++			*dst = ip6_route_output(net, sk, fl6);
++		rt = (*dst)->error ? NULL : (struct rt6_info *)*dst;
+ 		err = ip6_route_get_saddr(net, rt, &fl6->daddr,
+ 					  sk ? inet6_sk(sk)->srcprefs : 0,
+ 					  &fl6->saddr);
+ 		if (err)
+ 			goto out_err_release;
++
++		/* If we had an erroneous initial result, pretend it
++		 * never existed and let the SA-enabled version take
++		 * over.
++		 */
++		if (!had_dst && (*dst)->error) {
++			dst_release(*dst);
++			*dst = NULL;
++		}
+ 	}
+ 
++	if (!*dst)
++		*dst = ip6_route_output(net, sk, fl6);
++
++	err = (*dst)->error;
++	if (err)
++		goto out_err_release;
++
+ #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+ 	/*
+ 	 * Here if the dst entry we've looked up
+diff --git a/net/ipv6/route.c b/net/ipv6/route.c
+index 5c48293..d358888 100644
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -2245,9 +2245,10 @@ int ip6_route_get_saddr(struct net *net,
+ 			unsigned int prefs,
+ 			struct in6_addr *saddr)
+ {
+-	struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt);
++	struct inet6_dev *idev =
++		rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
+ 	int err = 0;
+-	if (rt->rt6i_prefsrc.plen)
++	if (rt && rt->rt6i_prefsrc.plen)
+ 		*saddr = rt->rt6i_prefsrc.addr;
+ 	else
+ 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
+-- 
+2.1.4
+
-- 
GitLab