diff options
author | Markus Stenberg <markus.stenberg@iki.fi> | 2015-05-05 06:36:59 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-05-09 15:58:41 -0400 |
commit | e16e888b525503be05b3aea64190e8b3bdef44d0 (patch) | |
tree | 7894cd9304ab18668c2d003bfa3e8c8c3a31e41f /net | |
parent | 0a801445db4cb00bc900fd89b1e5eb7d7b581d7e (diff) |
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/ip6_output.c | 39 | ||||
-rw-r--r-- | 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 7fde1f265c90..c21777565c58 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, | |||
886 | #endif | 886 | #endif |
887 | int err; | 887 | int err; |
888 | 888 | ||
889 | if (!*dst) | 889 | /* The correct way to handle this would be to do |
890 | *dst = ip6_route_output(net, sk, fl6); | 890 | * ip6_route_get_saddr, and then ip6_route_output; however, |
891 | 891 | * the route-specific preferred source forces the | |
892 | err = (*dst)->error; | 892 | * ip6_route_output call _before_ ip6_route_get_saddr. |
893 | if (err) | 893 | * |
894 | goto out_err_release; | 894 | * In source specific routing (no src=any default route), |
895 | * ip6_route_output will fail given src=any saddr, though, so | ||
896 | * that's why we try it again later. | ||
897 | */ | ||
898 | if (ipv6_addr_any(&fl6->saddr) && (!*dst || !(*dst)->error)) { | ||
899 | struct rt6_info *rt; | ||
900 | bool had_dst = *dst != NULL; | ||
895 | 901 | ||
896 | if (ipv6_addr_any(&fl6->saddr)) { | 902 | if (!had_dst) |
897 | struct rt6_info *rt = (struct rt6_info *) *dst; | 903 | *dst = ip6_route_output(net, sk, fl6); |
904 | rt = (*dst)->error ? NULL : (struct rt6_info *)*dst; | ||
898 | err = ip6_route_get_saddr(net, rt, &fl6->daddr, | 905 | err = ip6_route_get_saddr(net, rt, &fl6->daddr, |
899 | sk ? inet6_sk(sk)->srcprefs : 0, | 906 | sk ? inet6_sk(sk)->srcprefs : 0, |
900 | &fl6->saddr); | 907 | &fl6->saddr); |
901 | if (err) | 908 | if (err) |
902 | goto out_err_release; | 909 | goto out_err_release; |
910 | |||
911 | /* If we had an erroneous initial result, pretend it | ||
912 | * never existed and let the SA-enabled version take | ||
913 | * over. | ||
914 | */ | ||
915 | if (!had_dst && (*dst)->error) { | ||
916 | dst_release(*dst); | ||
917 | *dst = NULL; | ||
918 | } | ||
903 | } | 919 | } |
904 | 920 | ||
921 | if (!*dst) | ||
922 | *dst = ip6_route_output(net, sk, fl6); | ||
923 | |||
924 | err = (*dst)->error; | ||
925 | if (err) | ||
926 | goto out_err_release; | ||
927 | |||
905 | #ifdef CONFIG_IPV6_OPTIMISTIC_DAD | 928 | #ifdef CONFIG_IPV6_OPTIMISTIC_DAD |
906 | /* | 929 | /* |
907 | * Here if the dst entry we've looked up | 930 | * Here if the dst entry we've looked up |
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 5c48293ff062..d3588885f097 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -2245,9 +2245,10 @@ int ip6_route_get_saddr(struct net *net, | |||
2245 | unsigned int prefs, | 2245 | unsigned int prefs, |
2246 | struct in6_addr *saddr) | 2246 | struct in6_addr *saddr) |
2247 | { | 2247 | { |
2248 | struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt); | 2248 | struct inet6_dev *idev = |
2249 | rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL; | ||
2249 | int err = 0; | 2250 | int err = 0; |
2250 | if (rt->rt6i_prefsrc.plen) | 2251 | if (rt && rt->rt6i_prefsrc.plen) |
2251 | *saddr = rt->rt6i_prefsrc.addr; | 2252 | *saddr = rt->rt6i_prefsrc.addr; |
2252 | else | 2253 | else |
2253 | err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, | 2254 | err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, |