aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ip6_fib.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r--net/ipv6/ip6_fib.c115
1 files changed, 100 insertions, 15 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 93718f3db79b..424f063fb229 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -425,7 +425,8 @@ out:
425 425
426static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr, 426static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
427 int addrlen, int plen, 427 int addrlen, int plen,
428 int offset) 428 int offset, int allow_create,
429 int replace_required)
429{ 430{
430 struct fib6_node *fn, *in, *ln; 431 struct fib6_node *fn, *in, *ln;
431 struct fib6_node *pn = NULL; 432 struct fib6_node *pn = NULL;
@@ -447,8 +448,18 @@ static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
447 * Prefix match 448 * Prefix match
448 */ 449 */
449 if (plen < fn->fn_bit || 450 if (plen < fn->fn_bit ||
450 !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) 451 !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) {
452 if (!allow_create) {
453 if (replace_required) {
454 pr_warn("IPv6: Can't replace route, "
455 "no match found\n");
456 return ERR_PTR(-ENOENT);
457 }
458 pr_warn("IPv6: NLM_F_CREATE should be set "
459 "when creating new route\n");
460 }
451 goto insert_above; 461 goto insert_above;
462 }
452 463
453 /* 464 /*
454 * Exact match ? 465 * Exact match ?
@@ -477,6 +488,23 @@ static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
477 fn = dir ? fn->right: fn->left; 488 fn = dir ? fn->right: fn->left;
478 } while (fn); 489 } while (fn);
479 490
491 if (!allow_create) {
492 /* We should not create new node because
493 * NLM_F_REPLACE was specified without NLM_F_CREATE
494 * I assume it is safe to require NLM_F_CREATE when
495 * REPLACE flag is used! Later we may want to remove the
496 * check for replace_required, because according
497 * to netlink specification, NLM_F_CREATE
498 * MUST be specified if new route is created.
499 * That would keep IPv6 consistent with IPv4
500 */
501 if (replace_required) {
502 pr_warn("IPv6: Can't replace route, no match found\n");
503 return ERR_PTR(-ENOENT);
504 }
505 pr_warn("IPv6: NLM_F_CREATE should be set "
506 "when creating new route\n");
507 }
480 /* 508 /*
481 * We walked to the bottom of tree. 509 * We walked to the bottom of tree.
482 * Create new leaf node without children. 510 * Create new leaf node without children.
@@ -614,6 +642,11 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
614{ 642{
615 struct rt6_info *iter = NULL; 643 struct rt6_info *iter = NULL;
616 struct rt6_info **ins; 644 struct rt6_info **ins;
645 int replace = (NULL != info->nlh &&
646 (info->nlh->nlmsg_flags&NLM_F_REPLACE));
647 int add = (NULL == info->nlh ||
648 (info->nlh->nlmsg_flags&NLM_F_CREATE));
649 int found = 0;
617 650
618 ins = &fn->leaf; 651 ins = &fn->leaf;
619 652
@@ -626,6 +659,13 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
626 /* 659 /*
627 * Same priority level 660 * Same priority level
628 */ 661 */
662 if (NULL != info->nlh &&
663 (info->nlh->nlmsg_flags&NLM_F_EXCL))
664 return -EEXIST;
665 if (replace) {
666 found++;
667 break;
668 }
629 669
630 if (iter->rt6i_dev == rt->rt6i_dev && 670 if (iter->rt6i_dev == rt->rt6i_dev &&
631 iter->rt6i_idev == rt->rt6i_idev && 671 iter->rt6i_idev == rt->rt6i_idev &&
@@ -655,17 +695,40 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
655 /* 695 /*
656 * insert node 696 * insert node
657 */ 697 */
698 if (!replace) {
699 if (!add)
700 pr_warn("IPv6: NLM_F_CREATE should be set when creating new route\n");
701
702add:
703 rt->dst.rt6_next = iter;
704 *ins = rt;
705 rt->rt6i_node = fn;
706 atomic_inc(&rt->rt6i_ref);
707 inet6_rt_notify(RTM_NEWROUTE, rt, info);
708 info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
709
710 if ((fn->fn_flags & RTN_RTINFO) == 0) {
711 info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
712 fn->fn_flags |= RTN_RTINFO;
713 }
658 714
659 rt->dst.rt6_next = iter; 715 } else {
660 *ins = rt; 716 if (!found) {
661 rt->rt6i_node = fn; 717 if (add)
662 atomic_inc(&rt->rt6i_ref); 718 goto add;
663 inet6_rt_notify(RTM_NEWROUTE, rt, info); 719 pr_warn("IPv6: NLM_F_REPLACE set, but no existing node found!\n");
664 info->nl_net->ipv6.rt6_stats->fib_rt_entries++; 720 return -ENOENT;
665 721 }
666 if ((fn->fn_flags & RTN_RTINFO) == 0) { 722 *ins = rt;
667 info->nl_net->ipv6.rt6_stats->fib_route_nodes++; 723 rt->rt6i_node = fn;
668 fn->fn_flags |= RTN_RTINFO; 724 rt->dst.rt6_next = iter->dst.rt6_next;
725 atomic_inc(&rt->rt6i_ref);
726 inet6_rt_notify(RTM_NEWROUTE, rt, info);
727 rt6_release(iter);
728 if ((fn->fn_flags & RTN_RTINFO) == 0) {
729 info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
730 fn->fn_flags |= RTN_RTINFO;
731 }
669 } 732 }
670 733
671 return 0; 734 return 0;
@@ -696,9 +759,25 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
696{ 759{
697 struct fib6_node *fn, *pn = NULL; 760 struct fib6_node *fn, *pn = NULL;
698 int err = -ENOMEM; 761 int err = -ENOMEM;
762 int allow_create = 1;
763 int replace_required = 0;
764 if (NULL != info->nlh) {
765 if (!(info->nlh->nlmsg_flags&NLM_F_CREATE))
766 allow_create = 0;
767 if ((info->nlh->nlmsg_flags&NLM_F_REPLACE))
768 replace_required = 1;
769 }
770 if (!allow_create && !replace_required)
771 pr_warn("IPv6: RTM_NEWROUTE with no NLM_F_CREATE or NLM_F_REPLACE\n");
699 772
700 fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), 773 fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
701 rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst)); 774 rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst),
775 allow_create, replace_required);
776
777 if (IS_ERR(fn)) {
778 err = PTR_ERR(fn);
779 fn = NULL;
780 }
702 781
703 if (fn == NULL) 782 if (fn == NULL)
704 goto out; 783 goto out;
@@ -736,7 +815,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
736 815
737 sn = fib6_add_1(sfn, &rt->rt6i_src.addr, 816 sn = fib6_add_1(sfn, &rt->rt6i_src.addr,
738 sizeof(struct in6_addr), rt->rt6i_src.plen, 817 sizeof(struct in6_addr), rt->rt6i_src.plen,
739 offsetof(struct rt6_info, rt6i_src)); 818 offsetof(struct rt6_info, rt6i_src),
819 allow_create, replace_required);
740 820
741 if (sn == NULL) { 821 if (sn == NULL) {
742 /* If it is failed, discard just allocated 822 /* If it is failed, discard just allocated
@@ -753,8 +833,13 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
753 } else { 833 } else {
754 sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, 834 sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
755 sizeof(struct in6_addr), rt->rt6i_src.plen, 835 sizeof(struct in6_addr), rt->rt6i_src.plen,
756 offsetof(struct rt6_info, rt6i_src)); 836 offsetof(struct rt6_info, rt6i_src),
837 allow_create, replace_required);
757 838
839 if (IS_ERR(sn)) {
840 err = PTR_ERR(sn);
841 sn = NULL;
842 }
758 if (sn == NULL) 843 if (sn == NULL)
759 goto st_failure; 844 goto st_failure;
760 } 845 }