aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/route.c57
1 files changed, 25 insertions, 32 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index cd60bcca1064..061a7bba163a 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -397,10 +397,6 @@ int ip6_ins_rt(struct rt6_info *rt, struct nlmsghdr *nlh,
397 return err; 397 return err;
398} 398}
399 399
400/* No rt6_lock! If COW failed, the function returns dead route entry
401 with dst->error set to errno value.
402 */
403
404static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *daddr, 400static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *daddr,
405 struct in6_addr *saddr) 401 struct in6_addr *saddr)
406{ 402{
@@ -439,26 +435,6 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad
439 return rt; 435 return rt;
440} 436}
441 437
442static struct rt6_info *rt6_cow(struct rt6_info *ort, struct in6_addr *daddr,
443 struct in6_addr *saddr, struct netlink_skb_parms *req)
444{
445 struct rt6_info *rt = rt6_alloc_cow(ort, daddr, saddr);
446 int err;
447
448 if (!rt) {
449 dst_hold(&ip6_null_entry.u.dst);
450 return &ip6_null_entry;
451 }
452
453 dst_hold(&rt->u.dst);
454
455 err = ip6_ins_rt(rt, NULL, NULL, req);
456 if (err)
457 rt->u.dst.error = err;
458
459 return rt;
460}
461
462static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, struct in6_addr *daddr) 438static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, struct in6_addr *daddr)
463{ 439{
464 struct rt6_info *rt = ip6_rt_copy(ort); 440 struct rt6_info *rt = ip6_rt_copy(ort);
@@ -518,15 +494,23 @@ restart:
518 494
519 if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) { 495 if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) {
520 struct rt6_info *nrt; 496 struct rt6_info *nrt;
497 int err;
521 498
522 nrt = rt6_cow(rt, &skb->nh.ipv6h->daddr, 499 nrt = rt6_alloc_cow(rt, &skb->nh.ipv6h->daddr,
523 &skb->nh.ipv6h->saddr, 500 &skb->nh.ipv6h->saddr);
524 &NETLINK_CB(skb));
525 501
526 dst_release(&rt->u.dst); 502 dst_release(&rt->u.dst);
527 rt = nrt; 503 rt = nrt ? : &ip6_null_entry;
504
505 dst_hold(&rt->u.dst);
506 if (nrt) {
507 err = ip6_ins_rt(nrt, NULL, NULL,
508 &NETLINK_CB(skb));
509 if (!err)
510 goto out2;
511 }
528 512
529 if (rt->u.dst.error != -EEXIST || --attempts <= 0) 513 if (--attempts <= 0)
530 goto out2; 514 goto out2;
531 515
532 /* Race condition! In the gap, when rt6_lock was 516 /* Race condition! In the gap, when rt6_lock was
@@ -582,13 +566,21 @@ restart:
582 566
583 if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) { 567 if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) {
584 struct rt6_info *nrt; 568 struct rt6_info *nrt;
569 int err;
585 570
586 nrt = rt6_cow(rt, &fl->fl6_dst, &fl->fl6_src, NULL); 571 nrt = rt6_alloc_cow(rt, &fl->fl6_dst, &fl->fl6_src);
587 572
588 dst_release(&rt->u.dst); 573 dst_release(&rt->u.dst);
589 rt = nrt; 574 rt = nrt ? : &ip6_null_entry;
590 575
591 if (rt->u.dst.error != -EEXIST || --attempts <= 0) 576 dst_hold(&rt->u.dst);
577 if (nrt) {
578 err = ip6_ins_rt(nrt, NULL, NULL, NULL);
579 if (!err)
580 goto out2;
581 }
582
583 if (--attempts <= 0)
592 goto out2; 584 goto out2;
593 585
594 /* Race condition! In the gap, when rt6_lock was 586 /* Race condition! In the gap, when rt6_lock was
@@ -597,6 +589,7 @@ restart:
597 dst_release(&rt->u.dst); 589 dst_release(&rt->u.dst);
598 goto relookup; 590 goto relookup;
599 } 591 }
592
600out2: 593out2:
601 rt->u.dst.lastuse = jiffies; 594 rt->u.dst.lastuse = jiffies;
602 rt->u.dst.__use++; 595 rt->u.dst.__use++;