All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit a5a81f0b authored by Paul Marks's avatar Paul Marks Committed by David S. Miller

ipv6: Fix default route failover when CONFIG_IPV6_ROUTER_PREF=n

I believe this commit from 2008 was incorrect:
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=398bcbebb6f721ac308df1e3d658c0029bb74503

When CONFIG_IPV6_ROUTER_PREF is disabled, the kernel should follow
RFC4861 section 6.3.6: if no route is NUD_VALID, then traffic should be
sprayed across all routers (indirectly triggering NUD) until one of them
becomes NUD_VALID.

However, the following experiment demonstrates that this does not work:

1) Connect to an IPv6 network.
2) Change the router's MAC (and link-local) address.

The kernel will lock onto the first router and never try the new one, even
if the first becomes unreachable.  This patch fixes the problem by
allowing rt6_check_neigh() to return 0; if all routers return 0, then
rt6_select() will fall back to round-robin behavior.

This patch should have no effect when CONFIG_IPV6_ROUTER_PREF=y.

Note that rt6_check_neigh() is only used in a boolean context, so I've
changed its return type accordingly.
Signed-off-by: default avatarPaul Marks <pmarks@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5d097109
......@@ -544,35 +544,32 @@ static inline int rt6_check_dev(struct rt6_info *rt, int oif)
return 0;
}
static inline int rt6_check_neigh(struct rt6_info *rt)
static inline bool rt6_check_neigh(struct rt6_info *rt)
{
struct neighbour *neigh;
int m;
bool ret = false;
neigh = rt->n;
if (rt->rt6i_flags & RTF_NONEXTHOP ||
!(rt->rt6i_flags & RTF_GATEWAY))
m = 1;
ret = true;
else if (neigh) {
read_lock_bh(&neigh->lock);
if (neigh->nud_state & NUD_VALID)
m = 2;
ret = true;
#ifdef CONFIG_IPV6_ROUTER_PREF
else if (neigh->nud_state & NUD_FAILED)
m = 0;
else if (!(neigh->nud_state & NUD_FAILED))
ret = true;
#endif
else
m = 1;
read_unlock_bh(&neigh->lock);
} else
m = 0;
return m;
}
return ret;
}
static int rt6_score_route(struct rt6_info *rt, int oif,
int strict)
{
int m, n;
int m;
m = rt6_check_dev(rt, oif);
if (!m && (strict & RT6_LOOKUP_F_IFACE))
......@@ -580,8 +577,7 @@ static int rt6_score_route(struct rt6_info *rt, int oif,
#ifdef CONFIG_IPV6_ROUTER_PREF
m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
#endif
n = rt6_check_neigh(rt);
if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
if (!rt6_check_neigh(rt) && (strict & RT6_LOOKUP_F_REACHABLE))
return -1;
return m;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment