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) |
