aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorHannes Frederic Sowa <hannes@stressinduktion.org>2013-07-11 06:43:42 -0400
committerDavid S. Miller <davem@davemloft.net>2013-07-11 14:51:10 -0400
commitafc154e978de1eb11c555bc8bcec1552f75ebc43 (patch)
tree9398696864b0d770b0c108eda89dc4cf1bbf4482 /net/ipv6
parent1b4fc0e249d61916b8a525b0e7b3c028232457c9 (diff)
ipv6: fix route selection if kernel is not compiled with CONFIG_IPV6_ROUTER_PREF
This is a follow-up patch to 3630d40067a21d4dfbadc6002bb469ce26ac5d52 ("ipv6: rt6_check_neigh should successfully verify neigh if no NUD information are available"). Since the removal of rt->n in rt6_info we can end up with a dst == NULL in rt6_check_neigh. In case the kernel is not compiled with CONFIG_IPV6_ROUTER_PREF we should also select a route with unkown NUD state but we must not avoid doing round robin selection on routes with the same target. So introduce and pass down a boolean ``do_rr'' to indicate when we should update rt->rr_ptr. As soon as no route is valid we do backtracking and do a lookup on a higher level in the fib trie. v2: a) Improved rt6_check_neigh logic (no need to create neighbour there) and documented return values. v3: a) Introduce enum rt6_nud_state to get rid of the magic numbers (thanks to David Miller). b) Update and shorten commit message a bit to actualy reflect the source. Reported-by: Pierre Emeriaud <petrus.lt@gmail.com> Cc: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/route.c63
1 files changed, 40 insertions, 23 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 5b127e09c224..a8c891aa2464 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -65,6 +65,12 @@
65#include <linux/sysctl.h> 65#include <linux/sysctl.h>
66#endif 66#endif
67 67
68enum rt6_nud_state {
69 RT6_NUD_FAIL_HARD = -2,
70 RT6_NUD_FAIL_SOFT = -1,
71 RT6_NUD_SUCCEED = 1
72};
73
68static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 74static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
69 const struct in6_addr *dest); 75 const struct in6_addr *dest);
70static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 76static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
@@ -531,28 +537,29 @@ static inline int rt6_check_dev(struct rt6_info *rt, int oif)
531 return 0; 537 return 0;
532} 538}
533 539
534static inline bool rt6_check_neigh(struct rt6_info *rt) 540static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
535{ 541{
536 struct neighbour *neigh; 542 struct neighbour *neigh;
537 bool ret = false; 543 enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
538 544
539 if (rt->rt6i_flags & RTF_NONEXTHOP || 545 if (rt->rt6i_flags & RTF_NONEXTHOP ||
540 !(rt->rt6i_flags & RTF_GATEWAY)) 546 !(rt->rt6i_flags & RTF_GATEWAY))
541 return true; 547 return RT6_NUD_SUCCEED;
542 548
543 rcu_read_lock_bh(); 549 rcu_read_lock_bh();
544 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 550 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
545 if (neigh) { 551 if (neigh) {
546 read_lock(&neigh->lock); 552 read_lock(&neigh->lock);
547 if (neigh->nud_state & NUD_VALID) 553 if (neigh->nud_state & NUD_VALID)
548 ret = true; 554 ret = RT6_NUD_SUCCEED;
549#ifdef CONFIG_IPV6_ROUTER_PREF 555#ifdef CONFIG_IPV6_ROUTER_PREF
550 else if (!(neigh->nud_state & NUD_FAILED)) 556 else if (!(neigh->nud_state & NUD_FAILED))
551 ret = true; 557 ret = RT6_NUD_SUCCEED;
552#endif 558#endif
553 read_unlock(&neigh->lock); 559 read_unlock(&neigh->lock);
554 } else if (IS_ENABLED(CONFIG_IPV6_ROUTER_PREF)) { 560 } else {
555 ret = true; 561 ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
562 RT6_NUD_SUCCEED : RT6_NUD_FAIL_SOFT;
556 } 563 }
557 rcu_read_unlock_bh(); 564 rcu_read_unlock_bh();
558 565
@@ -566,43 +573,52 @@ static int rt6_score_route(struct rt6_info *rt, int oif,
566 573
567 m = rt6_check_dev(rt, oif); 574 m = rt6_check_dev(rt, oif);
568 if (!m && (strict & RT6_LOOKUP_F_IFACE)) 575 if (!m && (strict & RT6_LOOKUP_F_IFACE))
569 return -1; 576 return RT6_NUD_FAIL_HARD;
570#ifdef CONFIG_IPV6_ROUTER_PREF 577#ifdef CONFIG_IPV6_ROUTER_PREF
571 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 578 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
572#endif 579#endif
573 if (!rt6_check_neigh(rt) && (strict & RT6_LOOKUP_F_REACHABLE)) 580 if (strict & RT6_LOOKUP_F_REACHABLE) {
574 return -1; 581 int n = rt6_check_neigh(rt);
582 if (n < 0)
583 return n;
584 }
575 return m; 585 return m;
576} 586}
577 587
578static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 588static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
579 int *mpri, struct rt6_info *match) 589 int *mpri, struct rt6_info *match,
590 bool *do_rr)
580{ 591{
581 int m; 592 int m;
593 bool match_do_rr = false;
582 594
583 if (rt6_check_expired(rt)) 595 if (rt6_check_expired(rt))
584 goto out; 596 goto out;
585 597
586 m = rt6_score_route(rt, oif, strict); 598 m = rt6_score_route(rt, oif, strict);
587 if (m < 0) 599 if (m == RT6_NUD_FAIL_SOFT && !IS_ENABLED(CONFIG_IPV6_ROUTER_PREF)) {
600 match_do_rr = true;
601 m = 0; /* lowest valid score */
602 } else if (m < 0) {
588 goto out; 603 goto out;
604 }
605
606 if (strict & RT6_LOOKUP_F_REACHABLE)
607 rt6_probe(rt);
589 608
590 if (m > *mpri) { 609 if (m > *mpri) {
591 if (strict & RT6_LOOKUP_F_REACHABLE) 610 *do_rr = match_do_rr;
592 rt6_probe(match);
593 *mpri = m; 611 *mpri = m;
594 match = rt; 612 match = rt;
595 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
596 rt6_probe(rt);
597 } 613 }
598
599out: 614out:
600 return match; 615 return match;
601} 616}
602 617
603static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 618static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
604 struct rt6_info *rr_head, 619 struct rt6_info *rr_head,
605 u32 metric, int oif, int strict) 620 u32 metric, int oif, int strict,
621 bool *do_rr)
606{ 622{
607 struct rt6_info *rt, *match; 623 struct rt6_info *rt, *match;
608 int mpri = -1; 624 int mpri = -1;
@@ -610,10 +626,10 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
610 match = NULL; 626 match = NULL;
611 for (rt = rr_head; rt && rt->rt6i_metric == metric; 627 for (rt = rr_head; rt && rt->rt6i_metric == metric;
612 rt = rt->dst.rt6_next) 628 rt = rt->dst.rt6_next)
613 match = find_match(rt, oif, strict, &mpri, match); 629 match = find_match(rt, oif, strict, &mpri, match, do_rr);
614 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric; 630 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
615 rt = rt->dst.rt6_next) 631 rt = rt->dst.rt6_next)
616 match = find_match(rt, oif, strict, &mpri, match); 632 match = find_match(rt, oif, strict, &mpri, match, do_rr);
617 633
618 return match; 634 return match;
619} 635}
@@ -622,15 +638,16 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
622{ 638{
623 struct rt6_info *match, *rt0; 639 struct rt6_info *match, *rt0;
624 struct net *net; 640 struct net *net;
641 bool do_rr = false;
625 642
626 rt0 = fn->rr_ptr; 643 rt0 = fn->rr_ptr;
627 if (!rt0) 644 if (!rt0)
628 fn->rr_ptr = rt0 = fn->leaf; 645 fn->rr_ptr = rt0 = fn->leaf;
629 646
630 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict); 647 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
648 &do_rr);
631 649
632 if (!match && 650 if (do_rr) {
633 (strict & RT6_LOOKUP_F_REACHABLE)) {
634 struct rt6_info *next = rt0->dst.rt6_next; 651 struct rt6_info *next = rt0->dst.rt6_next;
635 652
636 /* no entries matched; do round-robin */ 653 /* no entries matched; do round-robin */