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: Paul Marks <pmarks@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
5d09710925
commit
a5a81f0b90
|
@ -544,35 +544,32 @@ static inline int rt6_check_dev(struct rt6_info *rt, int oif)
|
||||||
return 0;
|
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;
|
struct neighbour *neigh;
|
||||||
int m;
|
bool ret = false;
|
||||||
|
|
||||||
neigh = rt->n;
|
neigh = rt->n;
|
||||||
if (rt->rt6i_flags & RTF_NONEXTHOP ||
|
if (rt->rt6i_flags & RTF_NONEXTHOP ||
|
||||||
!(rt->rt6i_flags & RTF_GATEWAY))
|
!(rt->rt6i_flags & RTF_GATEWAY))
|
||||||
m = 1;
|
ret = true;
|
||||||
else if (neigh) {
|
else if (neigh) {
|
||||||
read_lock_bh(&neigh->lock);
|
read_lock_bh(&neigh->lock);
|
||||||
if (neigh->nud_state & NUD_VALID)
|
if (neigh->nud_state & NUD_VALID)
|
||||||
m = 2;
|
ret = true;
|
||||||
#ifdef CONFIG_IPV6_ROUTER_PREF
|
#ifdef CONFIG_IPV6_ROUTER_PREF
|
||||||
else if (neigh->nud_state & NUD_FAILED)
|
else if (!(neigh->nud_state & NUD_FAILED))
|
||||||
m = 0;
|
ret = true;
|
||||||
#endif
|
#endif
|
||||||
else
|
|
||||||
m = 1;
|
|
||||||
read_unlock_bh(&neigh->lock);
|
read_unlock_bh(&neigh->lock);
|
||||||
} else
|
}
|
||||||
m = 0;
|
return ret;
|
||||||
return m;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rt6_score_route(struct rt6_info *rt, int oif,
|
static int rt6_score_route(struct rt6_info *rt, int oif,
|
||||||
int strict)
|
int strict)
|
||||||
{
|
{
|
||||||
int m, n;
|
int m;
|
||||||
|
|
||||||
m = rt6_check_dev(rt, oif);
|
m = rt6_check_dev(rt, oif);
|
||||||
if (!m && (strict & RT6_LOOKUP_F_IFACE))
|
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
|
#ifdef CONFIG_IPV6_ROUTER_PREF
|
||||||
m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
|
m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
|
||||||
#endif
|
#endif
|
||||||
n = rt6_check_neigh(rt);
|
if (!rt6_check_neigh(rt) && (strict & RT6_LOOKUP_F_REACHABLE))
|
||||||
if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
|
|
||||||
return -1;
|
return -1;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue