diff options
author | Paul Marks <pmarks@google.com> | 2012-12-03 05:26:54 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-12-03 15:34:47 -0500 |
commit | a5a81f0b9025867efb999d14a8dfc1907c5a4c3b (patch) | |
tree | 4b2de223b5e08e0dcd4604d3619c738ff45eb2d3 /net/ipv6 | |
parent | 5d097109257c03a71845729f8db6b5770c4bbedc (diff) |
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>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/route.c | 24 |
1 files changed, 10 insertions, 14 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 8f124f575116..e229a3bc345d 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -544,35 +544,32 @@ static inline int rt6_check_dev(struct rt6_info *rt, int oif) | |||
544 | return 0; | 544 | return 0; |
545 | } | 545 | } |
546 | 546 | ||
547 | static inline int rt6_check_neigh(struct rt6_info *rt) | 547 | static inline bool rt6_check_neigh(struct rt6_info *rt) |
548 | { | 548 | { |
549 | struct neighbour *neigh; | 549 | struct neighbour *neigh; |
550 | int m; | 550 | bool ret = false; |
551 | 551 | ||
552 | neigh = rt->n; | 552 | neigh = rt->n; |
553 | if (rt->rt6i_flags & RTF_NONEXTHOP || | 553 | if (rt->rt6i_flags & RTF_NONEXTHOP || |
554 | !(rt->rt6i_flags & RTF_GATEWAY)) | 554 | !(rt->rt6i_flags & RTF_GATEWAY)) |
555 | m = 1; | 555 | ret = true; |
556 | else if (neigh) { | 556 | else if (neigh) { |
557 | read_lock_bh(&neigh->lock); | 557 | read_lock_bh(&neigh->lock); |
558 | if (neigh->nud_state & NUD_VALID) | 558 | if (neigh->nud_state & NUD_VALID) |
559 | m = 2; | 559 | ret = true; |
560 | #ifdef CONFIG_IPV6_ROUTER_PREF | 560 | #ifdef CONFIG_IPV6_ROUTER_PREF |
561 | else if (neigh->nud_state & NUD_FAILED) | 561 | else if (!(neigh->nud_state & NUD_FAILED)) |
562 | m = 0; | 562 | ret = true; |
563 | #endif | 563 | #endif |
564 | else | ||
565 | m = 1; | ||
566 | read_unlock_bh(&neigh->lock); | 564 | read_unlock_bh(&neigh->lock); |
567 | } else | 565 | } |
568 | m = 0; | 566 | return ret; |
569 | return m; | ||
570 | } | 567 | } |
571 | 568 | ||
572 | static int rt6_score_route(struct rt6_info *rt, int oif, | 569 | static int rt6_score_route(struct rt6_info *rt, int oif, |
573 | int strict) | 570 | int strict) |
574 | { | 571 | { |
575 | int m, n; | 572 | int m; |
576 | 573 | ||
577 | m = rt6_check_dev(rt, oif); | 574 | m = rt6_check_dev(rt, oif); |
578 | if (!m && (strict & RT6_LOOKUP_F_IFACE)) | 575 | if (!m && (strict & RT6_LOOKUP_F_IFACE)) |
@@ -580,8 +577,7 @@ static int rt6_score_route(struct rt6_info *rt, int oif, | |||
580 | #ifdef CONFIG_IPV6_ROUTER_PREF | 577 | #ifdef CONFIG_IPV6_ROUTER_PREF |
581 | m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; | 578 | m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; |
582 | #endif | 579 | #endif |
583 | n = rt6_check_neigh(rt); | 580 | if (!rt6_check_neigh(rt) && (strict & RT6_LOOKUP_F_REACHABLE)) |
584 | if (!n && (strict & RT6_LOOKUP_F_REACHABLE)) | ||
585 | return -1; | 581 | return -1; |
586 | return m; | 582 | return m; |
587 | } | 583 | } |