diff options
author | Matti Vaittinen <matti.vaittinen@nsn.com> | 2011-11-13 19:15:14 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-11-14 14:35:33 -0500 |
commit | 4a287eba2de395713d8b2b2aeaa69fa086832d34 (patch) | |
tree | 96962825dab1262bc46c8d5cc067103faea1d9ea /net/ipv6 | |
parent | d71314b4ac88637f9ac2770a9f635babdf6f2ff9 (diff) |
IPv6 routing, NLM_F_* flag support: REPLACE and EXCL flags support, warn about missing CREATE flag
The support for NLM_F_* flags at IPv6 routing requests.
If NLM_F_CREATE flag is not defined for RTM_NEWROUTE request,
warning is printed, but no error is returned. Instead new route is
added. Later NLM_F_CREATE may be required for
new route creation.
Exception is when NLM_F_REPLACE flag is given without NLM_F_CREATE, and
no matching route is found. In this case it should be safe to assume
that the request issuer is familiar with NLM_F_* flags, and does really
not want route to be created.
Specifying NLM_F_REPLACE flag will now make the kernel to search for
matching route, and replace it with new one. If no route is found and
NLM_F_CREATE is specified as well, then new route is created.
Also, specifying NLM_F_EXCL will yield returning of error if matching
route is found.
Patch created against linux-3.2-rc1
Signed-off-by: Matti Vaittinen <Mazziesaccount@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/ip6_fib.c | 109 |
1 files changed, 94 insertions, 15 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 93718f3db79b..9239d559b41b 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,12 @@ 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 | printk(KERN_WARNING | ||
454 | "IPv6: NLM_F_CREATE should be set when creating new route\n"); | ||
451 | goto insert_above; | 455 | goto insert_above; |
456 | } | ||
452 | 457 | ||
453 | /* | 458 | /* |
454 | * Exact match ? | 459 | * Exact match ? |
@@ -477,10 +482,26 @@ static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr, | |||
477 | fn = dir ? fn->right: fn->left; | 482 | fn = dir ? fn->right: fn->left; |
478 | } while (fn); | 483 | } while (fn); |
479 | 484 | ||
485 | if (replace_required && !allow_create) { | ||
486 | /* We should not create new node because | ||
487 | * NLM_F_REPLACE was specified without NLM_F_CREATE | ||
488 | * I assume it is safe to require NLM_F_CREATE when | ||
489 | * REPLACE flag is used! Later we may want to remove the | ||
490 | * check for replace_required, because according | ||
491 | * to netlink specification, NLM_F_CREATE | ||
492 | * MUST be specified if new route is created. | ||
493 | * That would keep IPv6 consistent with IPv4 | ||
494 | */ | ||
495 | printk(KERN_WARNING | ||
496 | "IPv6: NLM_F_CREATE should be set when creating new route - ignoring request\n"); | ||
497 | return ERR_PTR(-ENOENT); | ||
498 | } | ||
480 | /* | 499 | /* |
481 | * We walked to the bottom of tree. | 500 | * We walked to the bottom of tree. |
482 | * Create new leaf node without children. | 501 | * Create new leaf node without children. |
483 | */ | 502 | */ |
503 | if (!allow_create) | ||
504 | printk(KERN_WARNING "IPv6: NLM_F_CREATE should be set when creating new route\n"); | ||
484 | 505 | ||
485 | ln = node_alloc(); | 506 | ln = node_alloc(); |
486 | 507 | ||
@@ -614,6 +635,12 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, | |||
614 | { | 635 | { |
615 | struct rt6_info *iter = NULL; | 636 | struct rt6_info *iter = NULL; |
616 | struct rt6_info **ins; | 637 | struct rt6_info **ins; |
638 | int replace = (NULL != info && | ||
639 | NULL != info->nlh && | ||
640 | (info->nlh->nlmsg_flags&NLM_F_REPLACE)); | ||
641 | int add = ((NULL == info || NULL == info->nlh) || | ||
642 | (info->nlh->nlmsg_flags&NLM_F_CREATE)); | ||
643 | int found = 0; | ||
617 | 644 | ||
618 | ins = &fn->leaf; | 645 | ins = &fn->leaf; |
619 | 646 | ||
@@ -626,6 +653,13 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, | |||
626 | /* | 653 | /* |
627 | * Same priority level | 654 | * Same priority level |
628 | */ | 655 | */ |
656 | if (NULL != info->nlh && | ||
657 | (info->nlh->nlmsg_flags&NLM_F_EXCL)) | ||
658 | return -EEXIST; | ||
659 | if (replace) { | ||
660 | found++; | ||
661 | break; | ||
662 | } | ||
629 | 663 | ||
630 | if (iter->rt6i_dev == rt->rt6i_dev && | 664 | if (iter->rt6i_dev == rt->rt6i_dev && |
631 | iter->rt6i_idev == rt->rt6i_idev && | 665 | iter->rt6i_idev == rt->rt6i_idev && |
@@ -655,17 +689,40 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, | |||
655 | /* | 689 | /* |
656 | * insert node | 690 | * insert node |
657 | */ | 691 | */ |
692 | if (!replace) { | ||
693 | if (!add) | ||
694 | printk(KERN_WARNING "IPv6: NLM_F_CREATE should be set when creating new route\n"); | ||
695 | |||
696 | add: | ||
697 | rt->dst.rt6_next = iter; | ||
698 | *ins = rt; | ||
699 | rt->rt6i_node = fn; | ||
700 | atomic_inc(&rt->rt6i_ref); | ||
701 | inet6_rt_notify(RTM_NEWROUTE, rt, info); | ||
702 | info->nl_net->ipv6.rt6_stats->fib_rt_entries++; | ||
703 | |||
704 | if ((fn->fn_flags & RTN_RTINFO) == 0) { | ||
705 | info->nl_net->ipv6.rt6_stats->fib_route_nodes++; | ||
706 | fn->fn_flags |= RTN_RTINFO; | ||
707 | } | ||
658 | 708 | ||
659 | rt->dst.rt6_next = iter; | 709 | } else { |
660 | *ins = rt; | 710 | if (!found) { |
661 | rt->rt6i_node = fn; | 711 | if (add) |
662 | atomic_inc(&rt->rt6i_ref); | 712 | goto add; |
663 | inet6_rt_notify(RTM_NEWROUTE, rt, info); | 713 | printk(KERN_WARNING "IPv6: NLM_F_REPLACE set, but no existing node found!\n"); |
664 | info->nl_net->ipv6.rt6_stats->fib_rt_entries++; | 714 | return -ENOENT; |
665 | 715 | } | |
666 | if ((fn->fn_flags & RTN_RTINFO) == 0) { | 716 | *ins = rt; |
667 | info->nl_net->ipv6.rt6_stats->fib_route_nodes++; | 717 | rt->rt6i_node = fn; |
668 | fn->fn_flags |= RTN_RTINFO; | 718 | rt->dst.rt6_next = iter->dst.rt6_next; |
719 | atomic_inc(&rt->rt6i_ref); | ||
720 | inet6_rt_notify(RTM_NEWROUTE, rt, info); | ||
721 | rt6_release(iter); | ||
722 | if ((fn->fn_flags & RTN_RTINFO) == 0) { | ||
723 | info->nl_net->ipv6.rt6_stats->fib_route_nodes++; | ||
724 | fn->fn_flags |= RTN_RTINFO; | ||
725 | } | ||
669 | } | 726 | } |
670 | 727 | ||
671 | return 0; | 728 | return 0; |
@@ -696,9 +753,25 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) | |||
696 | { | 753 | { |
697 | struct fib6_node *fn, *pn = NULL; | 754 | struct fib6_node *fn, *pn = NULL; |
698 | int err = -ENOMEM; | 755 | int err = -ENOMEM; |
756 | int allow_create = 1; | ||
757 | int replace_required = 0; | ||
758 | if (NULL != info && NULL != info->nlh) { | ||
759 | if (!(info->nlh->nlmsg_flags&NLM_F_CREATE)) | ||
760 | allow_create = 0; | ||
761 | if ((info->nlh->nlmsg_flags&NLM_F_REPLACE)) | ||
762 | replace_required = 1; | ||
763 | } | ||
764 | if (!allow_create && !replace_required) | ||
765 | printk(KERN_WARNING "IPv6: RTM_NEWROUTE with no NLM_F_CREATE or NLM_F_REPLACE\n"); | ||
699 | 766 | ||
700 | fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), | 767 | fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), |
701 | rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst)); | 768 | rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst), |
769 | allow_create, replace_required); | ||
770 | |||
771 | if (IS_ERR(fn)) { | ||
772 | err = PTR_ERR(fn); | ||
773 | fn = NULL; | ||
774 | } | ||
702 | 775 | ||
703 | if (fn == NULL) | 776 | if (fn == NULL) |
704 | goto out; | 777 | goto out; |
@@ -736,7 +809,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) | |||
736 | 809 | ||
737 | sn = fib6_add_1(sfn, &rt->rt6i_src.addr, | 810 | sn = fib6_add_1(sfn, &rt->rt6i_src.addr, |
738 | sizeof(struct in6_addr), rt->rt6i_src.plen, | 811 | sizeof(struct in6_addr), rt->rt6i_src.plen, |
739 | offsetof(struct rt6_info, rt6i_src)); | 812 | offsetof(struct rt6_info, rt6i_src), |
813 | allow_create, replace_required); | ||
740 | 814 | ||
741 | if (sn == NULL) { | 815 | if (sn == NULL) { |
742 | /* If it is failed, discard just allocated | 816 | /* If it is failed, discard just allocated |
@@ -753,8 +827,13 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) | |||
753 | } else { | 827 | } else { |
754 | sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, | 828 | sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, |
755 | sizeof(struct in6_addr), rt->rt6i_src.plen, | 829 | sizeof(struct in6_addr), rt->rt6i_src.plen, |
756 | offsetof(struct rt6_info, rt6i_src)); | 830 | offsetof(struct rt6_info, rt6i_src), |
831 | allow_create, replace_required); | ||
757 | 832 | ||
833 | if (IS_ERR(sn)) { | ||
834 | err = PTR_ERR(sn); | ||
835 | sn = NULL; | ||
836 | } | ||
758 | if (sn == NULL) | 837 | if (sn == NULL) |
759 | goto st_failure; | 838 | goto st_failure; |
760 | } | 839 | } |