Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Matti Vaittinen and committed by
David S. Miller
4a287eba d71314b4

+93 -14
+93 -14
net/ipv6/ip6_fib.c
··· 425 425 426 426 static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr, 427 427 int addrlen, int plen, 428 - int offset) 428 + int offset, int allow_create, 429 + int replace_required) 429 430 { 430 431 struct fib6_node *fn, *in, *ln; 431 432 struct fib6_node *pn = NULL; ··· 448 447 * Prefix match 449 448 */ 450 449 if (plen < fn->fn_bit || 451 - !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) 450 + !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) { 451 + if (!allow_create) 452 + printk(KERN_WARNING 453 + "IPv6: NLM_F_CREATE should be set when creating new route\n"); 452 454 goto insert_above; 455 + } 453 456 454 457 /* 455 458 * Exact match ? ··· 482 477 fn = dir ? fn->right: fn->left; 483 478 } while (fn); 484 479 480 + if (replace_required && !allow_create) { 481 + /* We should not create new node because 482 + * NLM_F_REPLACE was specified without NLM_F_CREATE 483 + * I assume it is safe to require NLM_F_CREATE when 484 + * REPLACE flag is used! Later we may want to remove the 485 + * check for replace_required, because according 486 + * to netlink specification, NLM_F_CREATE 487 + * MUST be specified if new route is created. 488 + * That would keep IPv6 consistent with IPv4 489 + */ 490 + printk(KERN_WARNING 491 + "IPv6: NLM_F_CREATE should be set when creating new route - ignoring request\n"); 492 + return ERR_PTR(-ENOENT); 493 + } 485 494 /* 486 495 * We walked to the bottom of tree. 487 496 * Create new leaf node without children. 488 497 */ 498 + if (!allow_create) 499 + printk(KERN_WARNING "IPv6: NLM_F_CREATE should be set when creating new route\n"); 489 500 490 501 ln = node_alloc(); 491 502 ··· 635 614 { 636 615 struct rt6_info *iter = NULL; 637 616 struct rt6_info **ins; 617 + int replace = (NULL != info && 618 + NULL != info->nlh && 619 + (info->nlh->nlmsg_flags&NLM_F_REPLACE)); 620 + int add = ((NULL == info || NULL == info->nlh) || 621 + (info->nlh->nlmsg_flags&NLM_F_CREATE)); 622 + int found = 0; 638 623 639 624 ins = &fn->leaf; 640 625 ··· 653 626 /* 654 627 * Same priority level 655 628 */ 629 + if (NULL != info->nlh && 630 + (info->nlh->nlmsg_flags&NLM_F_EXCL)) 631 + return -EEXIST; 632 + if (replace) { 633 + found++; 634 + break; 635 + } 656 636 657 637 if (iter->rt6i_dev == rt->rt6i_dev && 658 638 iter->rt6i_idev == rt->rt6i_idev && ··· 689 655 /* 690 656 * insert node 691 657 */ 658 + if (!replace) { 659 + if (!add) 660 + printk(KERN_WARNING "IPv6: NLM_F_CREATE should be set when creating new route\n"); 692 661 693 - rt->dst.rt6_next = iter; 694 - *ins = rt; 695 - rt->rt6i_node = fn; 696 - atomic_inc(&rt->rt6i_ref); 697 - inet6_rt_notify(RTM_NEWROUTE, rt, info); 698 - info->nl_net->ipv6.rt6_stats->fib_rt_entries++; 662 + add: 663 + rt->dst.rt6_next = iter; 664 + *ins = rt; 665 + rt->rt6i_node = fn; 666 + atomic_inc(&rt->rt6i_ref); 667 + inet6_rt_notify(RTM_NEWROUTE, rt, info); 668 + info->nl_net->ipv6.rt6_stats->fib_rt_entries++; 699 669 700 - if ((fn->fn_flags & RTN_RTINFO) == 0) { 701 - info->nl_net->ipv6.rt6_stats->fib_route_nodes++; 702 - fn->fn_flags |= RTN_RTINFO; 670 + if ((fn->fn_flags & RTN_RTINFO) == 0) { 671 + info->nl_net->ipv6.rt6_stats->fib_route_nodes++; 672 + fn->fn_flags |= RTN_RTINFO; 673 + } 674 + 675 + } else { 676 + if (!found) { 677 + if (add) 678 + goto add; 679 + printk(KERN_WARNING "IPv6: NLM_F_REPLACE set, but no existing node found!\n"); 680 + return -ENOENT; 681 + } 682 + *ins = rt; 683 + rt->rt6i_node = fn; 684 + rt->dst.rt6_next = iter->dst.rt6_next; 685 + atomic_inc(&rt->rt6i_ref); 686 + inet6_rt_notify(RTM_NEWROUTE, rt, info); 687 + rt6_release(iter); 688 + if ((fn->fn_flags & RTN_RTINFO) == 0) { 689 + info->nl_net->ipv6.rt6_stats->fib_route_nodes++; 690 + fn->fn_flags |= RTN_RTINFO; 691 + } 703 692 } 704 693 705 694 return 0; ··· 753 696 { 754 697 struct fib6_node *fn, *pn = NULL; 755 698 int err = -ENOMEM; 699 + int allow_create = 1; 700 + int replace_required = 0; 701 + if (NULL != info && NULL != info->nlh) { 702 + if (!(info->nlh->nlmsg_flags&NLM_F_CREATE)) 703 + allow_create = 0; 704 + if ((info->nlh->nlmsg_flags&NLM_F_REPLACE)) 705 + replace_required = 1; 706 + } 707 + if (!allow_create && !replace_required) 708 + printk(KERN_WARNING "IPv6: RTM_NEWROUTE with no NLM_F_CREATE or NLM_F_REPLACE\n"); 756 709 757 710 fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), 758 - rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst)); 711 + rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst), 712 + allow_create, replace_required); 713 + 714 + if (IS_ERR(fn)) { 715 + err = PTR_ERR(fn); 716 + fn = NULL; 717 + } 759 718 760 719 if (fn == NULL) 761 720 goto out; ··· 809 736 810 737 sn = fib6_add_1(sfn, &rt->rt6i_src.addr, 811 738 sizeof(struct in6_addr), rt->rt6i_src.plen, 812 - offsetof(struct rt6_info, rt6i_src)); 739 + offsetof(struct rt6_info, rt6i_src), 740 + allow_create, replace_required); 813 741 814 742 if (sn == NULL) { 815 743 /* If it is failed, discard just allocated ··· 827 753 } else { 828 754 sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, 829 755 sizeof(struct in6_addr), rt->rt6i_src.plen, 830 - offsetof(struct rt6_info, rt6i_src)); 756 + offsetof(struct rt6_info, rt6i_src), 757 + allow_create, replace_required); 831 758 759 + if (IS_ERR(sn)) { 760 + err = PTR_ERR(sn); 761 + sn = NULL; 762 + } 832 763 if (sn == NULL) 833 764 goto st_failure; 834 765 }