From: Steven Barth Date: Tue, 19 May 2015 07:43:59 +0000 (+0000) Subject: linux: backport IPv6 SAS fixes for source-specific routes X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=12969569086d18bd6ee4235224ea098607e860b9;p=openwrt%2Fstaging%2Flinusw.git linux: backport IPv6 SAS fixes for source-specific routes Signed-off-by: Steven Barth SVN-Revision: 45699 --- 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 +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 +Signed-off-by: David S. Miller +--- + 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 +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 +Signed-off-by: David S. Miller +--- + 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 +