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/route.c | |
| 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/route.c')
| -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 | } |
