Skip to content

Commit e16e888

Browse files
fingondavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0a80144 commit e16e888

File tree

2 files changed

+34
-10
lines changed

2 files changed

+34
-10
lines changed

net/ipv6/ip6_output.c

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -886,22 +886,45 @@ static int ip6_dst_lookup_tail(struct sock *sk,
886886
#endif
887887
int err;
888888

889-
if (!*dst)
890-
*dst = ip6_route_output(net, sk, fl6);
891-
892-
err = (*dst)->error;
893-
if (err)
894-
goto out_err_release;
889+
/* The correct way to handle this would be to do
890+
* ip6_route_get_saddr, and then ip6_route_output; however,
891+
* the route-specific preferred source forces the
892+
* ip6_route_output call _before_ ip6_route_get_saddr.
893+
*
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;
895901

896-
if (ipv6_addr_any(&fl6->saddr)) {
897-
struct rt6_info *rt = (struct rt6_info *) *dst;
902+
if (!had_dst)
903+
*dst = ip6_route_output(net, sk, fl6);
904+
rt = (*dst)->error ? NULL : (struct rt6_info *)*dst;
898905
err = ip6_route_get_saddr(net, rt, &fl6->daddr,
899906
sk ? inet6_sk(sk)->srcprefs : 0,
900907
&fl6->saddr);
901908
if (err)
902909
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+
}
903919
}
904920

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+
905928
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
906929
/*
907930
* Here if the dst entry we've looked up

net/ipv6/route.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2245,9 +2245,10 @@ int ip6_route_get_saddr(struct net *net,
22452245
unsigned int prefs,
22462246
struct in6_addr *saddr)
22472247
{
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;
22492250
int err = 0;
2250-
if (rt->rt6i_prefsrc.plen)
2251+
if (rt && rt->rt6i_prefsrc.plen)
22512252
*saddr = rt->rt6i_prefsrc.addr;
22522253
else
22532254
err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,

0 commit comments

Comments
 (0)