diff options
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r-- | net/ipv6/ip6_fib.c | 115 |
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 | ||
426 | static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr, | 426 | static 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 | |||
702 | add: | ||
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 | } |