diff options
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r-- | net/ipv6/route.c | 97 |
1 files changed, 59 insertions, 38 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index a6b3117df546..3931b33b25e8 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -363,55 +363,76 @@ static int rt6_score_route(struct rt6_info *rt, int oif, | |||
363 | return m; | 363 | return m; |
364 | } | 364 | } |
365 | 365 | ||
366 | static struct rt6_info *rt6_select(struct rt6_info **head, int oif, | 366 | static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, |
367 | int strict) | 367 | int *mpri, struct rt6_info *match) |
368 | { | 368 | { |
369 | struct rt6_info *match = NULL, *last = NULL; | 369 | int m; |
370 | struct rt6_info *rt, *rt0 = *head; | 370 | |
371 | u32 metric; | 371 | if (rt6_check_expired(rt)) |
372 | goto out; | ||
373 | |||
374 | m = rt6_score_route(rt, oif, strict); | ||
375 | if (m < 0) | ||
376 | goto out; | ||
377 | |||
378 | if (m > *mpri) { | ||
379 | if (strict & RT6_LOOKUP_F_REACHABLE) | ||
380 | rt6_probe(match); | ||
381 | *mpri = m; | ||
382 | match = rt; | ||
383 | } else if (strict & RT6_LOOKUP_F_REACHABLE) { | ||
384 | rt6_probe(rt); | ||
385 | } | ||
386 | |||
387 | out: | ||
388 | return match; | ||
389 | } | ||
390 | |||
391 | static struct rt6_info *find_rr_leaf(struct fib6_node *fn, | ||
392 | struct rt6_info *rr_head, | ||
393 | u32 metric, int oif, int strict) | ||
394 | { | ||
395 | struct rt6_info *rt, *match; | ||
372 | int mpri = -1; | 396 | int mpri = -1; |
373 | 397 | ||
374 | RT6_TRACE("%s(head=%p(*head=%p), oif=%d)\n", | 398 | match = NULL; |
375 | __FUNCTION__, head, head ? *head : NULL, oif); | 399 | for (rt = rr_head; rt && rt->rt6i_metric == metric; |
400 | rt = rt->u.dst.rt6_next) | ||
401 | match = find_match(rt, oif, strict, &mpri, match); | ||
402 | for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric; | ||
403 | rt = rt->u.dst.rt6_next) | ||
404 | match = find_match(rt, oif, strict, &mpri, match); | ||
376 | 405 | ||
377 | for (rt = rt0, metric = rt0->rt6i_metric; | 406 | return match; |
378 | rt && rt->rt6i_metric == metric && (!last || rt != rt0); | 407 | } |
379 | rt = rt->u.dst.rt6_next) { | ||
380 | int m; | ||
381 | 408 | ||
382 | if (rt6_check_expired(rt)) | 409 | static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) |
383 | continue; | 410 | { |
411 | struct rt6_info *match, *rt0; | ||
384 | 412 | ||
385 | last = rt; | 413 | RT6_TRACE("%s(fn->leaf=%p, oif=%d)\n", |
414 | __FUNCTION__, fn->leaf, oif); | ||
386 | 415 | ||
387 | m = rt6_score_route(rt, oif, strict); | 416 | rt0 = fn->rr_ptr; |
388 | if (m < 0) | 417 | if (!rt0) |
389 | continue; | 418 | fn->rr_ptr = rt0 = fn->leaf; |
390 | 419 | ||
391 | if (m > mpri) { | 420 | match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict); |
392 | if (strict & RT6_LOOKUP_F_REACHABLE) | ||
393 | rt6_probe(match); | ||
394 | match = rt; | ||
395 | mpri = m; | ||
396 | } else if (strict & RT6_LOOKUP_F_REACHABLE) { | ||
397 | rt6_probe(rt); | ||
398 | } | ||
399 | } | ||
400 | 421 | ||
401 | if (!match && | 422 | if (!match && |
402 | (strict & RT6_LOOKUP_F_REACHABLE) && | 423 | (strict & RT6_LOOKUP_F_REACHABLE)) { |
403 | last && last != rt0) { | 424 | struct rt6_info *next = rt0->u.dst.rt6_next; |
425 | |||
404 | /* no entries matched; do round-robin */ | 426 | /* no entries matched; do round-robin */ |
405 | static DEFINE_SPINLOCK(lock); | 427 | if (!next || next->rt6i_metric != rt0->rt6i_metric) |
406 | spin_lock(&lock); | 428 | next = fn->leaf; |
407 | *head = rt0->u.dst.rt6_next; | 429 | |
408 | rt0->u.dst.rt6_next = last->u.dst.rt6_next; | 430 | if (next != rt0) |
409 | last->u.dst.rt6_next = rt0; | 431 | fn->rr_ptr = next; |
410 | spin_unlock(&lock); | ||
411 | } | 432 | } |
412 | 433 | ||
413 | RT6_TRACE("%s() => %p, score=%d\n", | 434 | RT6_TRACE("%s() => %p\n", |
414 | __FUNCTION__, match, mpri); | 435 | __FUNCTION__, match); |
415 | 436 | ||
416 | return (match ? match : &ip6_null_entry); | 437 | return (match ? match : &ip6_null_entry); |
417 | } | 438 | } |
@@ -657,7 +678,7 @@ restart_2: | |||
657 | fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src); | 678 | fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src); |
658 | 679 | ||
659 | restart: | 680 | restart: |
660 | rt = rt6_select(&fn->leaf, fl->iif, strict | reachable); | 681 | rt = rt6_select(fn, fl->iif, strict | reachable); |
661 | BACKTRACK(&fl->fl6_src); | 682 | BACKTRACK(&fl->fl6_src); |
662 | if (rt == &ip6_null_entry || | 683 | if (rt == &ip6_null_entry || |
663 | rt->rt6i_flags & RTF_CACHE) | 684 | rt->rt6i_flags & RTF_CACHE) |
@@ -752,7 +773,7 @@ restart_2: | |||
752 | fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src); | 773 | fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src); |
753 | 774 | ||
754 | restart: | 775 | restart: |
755 | rt = rt6_select(&fn->leaf, fl->oif, strict | reachable); | 776 | rt = rt6_select(fn, fl->oif, strict | reachable); |
756 | BACKTRACK(&fl->fl6_src); | 777 | BACKTRACK(&fl->fl6_src); |
757 | if (rt == &ip6_null_entry || | 778 | if (rt == &ip6_null_entry || |
758 | rt->rt6i_flags & RTF_CACHE) | 779 | rt->rt6i_flags & RTF_CACHE) |