diff options
-rw-r--r-- | include/net/ip6_fib.h | 10 | ||||
-rw-r--r-- | net/ipv6/ip6_fib.c | 69 | ||||
-rw-r--r-- | net/ipv6/route.c | 57 |
3 files changed, 90 insertions, 46 deletions
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 8eea35d32a75..20e80fa7bbdd 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h | |||
@@ -74,6 +74,11 @@ struct fib6_node { | |||
74 | #define FIB6_SUBTREE(fn) ((fn)->subtree) | 74 | #define FIB6_SUBTREE(fn) ((fn)->subtree) |
75 | #endif | 75 | #endif |
76 | 76 | ||
77 | struct mx6_config { | ||
78 | const u32 *mx; | ||
79 | DECLARE_BITMAP(mx_valid, RTAX_MAX); | ||
80 | }; | ||
81 | |||
77 | /* | 82 | /* |
78 | * routing information | 83 | * routing information |
79 | * | 84 | * |
@@ -291,9 +296,8 @@ struct fib6_node *fib6_locate(struct fib6_node *root, | |||
291 | void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), | 296 | void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), |
292 | void *arg); | 297 | void *arg); |
293 | 298 | ||
294 | int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info, | 299 | int fib6_add(struct fib6_node *root, struct rt6_info *rt, |
295 | struct nlattr *mx, int mx_len); | 300 | struct nl_info *info, struct mx6_config *mxc); |
296 | |||
297 | int fib6_del(struct rt6_info *rt, struct nl_info *info); | 301 | int fib6_del(struct rt6_info *rt, struct nl_info *info); |
298 | 302 | ||
299 | void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info); | 303 | void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info); |
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index db4984e13f2f..03c520a4ebeb 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c | |||
@@ -630,31 +630,35 @@ static bool rt6_qualify_for_ecmp(struct rt6_info *rt) | |||
630 | RTF_GATEWAY; | 630 | RTF_GATEWAY; |
631 | } | 631 | } |
632 | 632 | ||
633 | static int fib6_commit_metrics(struct dst_entry *dst, | 633 | static void fib6_copy_metrics(u32 *mp, const struct mx6_config *mxc) |
634 | struct nlattr *mx, int mx_len) | ||
635 | { | 634 | { |
636 | bool dst_host = dst->flags & DST_HOST; | 635 | int i; |
637 | struct nlattr *nla; | ||
638 | int remaining; | ||
639 | u32 *mp; | ||
640 | 636 | ||
641 | mp = dst_host ? dst_metrics_write_ptr(dst) : | 637 | for (i = 0; i < RTAX_MAX; i++) { |
642 | kzalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC); | 638 | if (test_bit(i, mxc->mx_valid)) |
643 | if (unlikely(!mp)) | 639 | mp[i] = mxc->mx[i]; |
644 | return -ENOMEM; | 640 | } |
645 | if (!dst_host) | 641 | } |
646 | dst_init_metrics(dst, mp, 0); | 642 | |
643 | static int fib6_commit_metrics(struct dst_entry *dst, struct mx6_config *mxc) | ||
644 | { | ||
645 | if (!mxc->mx) | ||
646 | return 0; | ||
647 | 647 | ||
648 | nla_for_each_attr(nla, mx, mx_len, remaining) { | 648 | if (dst->flags & DST_HOST) { |
649 | int type = nla_type(nla); | 649 | u32 *mp = dst_metrics_write_ptr(dst); |
650 | 650 | ||
651 | if (type) { | 651 | if (unlikely(!mp)) |
652 | if (type > RTAX_MAX) | 652 | return -ENOMEM; |
653 | return -EINVAL; | ||
654 | 653 | ||
655 | mp[type - 1] = nla_get_u32(nla); | 654 | fib6_copy_metrics(mp, mxc); |
656 | } | 655 | } else { |
656 | dst_init_metrics(dst, mxc->mx, false); | ||
657 | |||
658 | /* We've stolen mx now. */ | ||
659 | mxc->mx = NULL; | ||
657 | } | 660 | } |
661 | |||
658 | return 0; | 662 | return 0; |
659 | } | 663 | } |
660 | 664 | ||
@@ -663,7 +667,7 @@ static int fib6_commit_metrics(struct dst_entry *dst, | |||
663 | */ | 667 | */ |
664 | 668 | ||
665 | static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, | 669 | static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, |
666 | struct nl_info *info, struct nlattr *mx, int mx_len) | 670 | struct nl_info *info, struct mx6_config *mxc) |
667 | { | 671 | { |
668 | struct rt6_info *iter = NULL; | 672 | struct rt6_info *iter = NULL; |
669 | struct rt6_info **ins; | 673 | struct rt6_info **ins; |
@@ -772,11 +776,10 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, | |||
772 | pr_warn("NLM_F_CREATE should be set when creating new route\n"); | 776 | pr_warn("NLM_F_CREATE should be set when creating new route\n"); |
773 | 777 | ||
774 | add: | 778 | add: |
775 | if (mx) { | 779 | err = fib6_commit_metrics(&rt->dst, mxc); |
776 | err = fib6_commit_metrics(&rt->dst, mx, mx_len); | 780 | if (err) |
777 | if (err) | 781 | return err; |
778 | return err; | 782 | |
779 | } | ||
780 | rt->dst.rt6_next = iter; | 783 | rt->dst.rt6_next = iter; |
781 | *ins = rt; | 784 | *ins = rt; |
782 | rt->rt6i_node = fn; | 785 | rt->rt6i_node = fn; |
@@ -796,11 +799,11 @@ add: | |||
796 | pr_warn("NLM_F_REPLACE set, but no existing node found!\n"); | 799 | pr_warn("NLM_F_REPLACE set, but no existing node found!\n"); |
797 | return -ENOENT; | 800 | return -ENOENT; |
798 | } | 801 | } |
799 | if (mx) { | 802 | |
800 | err = fib6_commit_metrics(&rt->dst, mx, mx_len); | 803 | err = fib6_commit_metrics(&rt->dst, mxc); |
801 | if (err) | 804 | if (err) |
802 | return err; | 805 | return err; |
803 | } | 806 | |
804 | *ins = rt; | 807 | *ins = rt; |
805 | rt->rt6i_node = fn; | 808 | rt->rt6i_node = fn; |
806 | rt->dst.rt6_next = iter->dst.rt6_next; | 809 | rt->dst.rt6_next = iter->dst.rt6_next; |
@@ -837,8 +840,8 @@ void fib6_force_start_gc(struct net *net) | |||
837 | * with source addr info in sub-trees | 840 | * with source addr info in sub-trees |
838 | */ | 841 | */ |
839 | 842 | ||
840 | int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info, | 843 | int fib6_add(struct fib6_node *root, struct rt6_info *rt, |
841 | struct nlattr *mx, int mx_len) | 844 | struct nl_info *info, struct mx6_config *mxc) |
842 | { | 845 | { |
843 | struct fib6_node *fn, *pn = NULL; | 846 | struct fib6_node *fn, *pn = NULL; |
844 | int err = -ENOMEM; | 847 | int err = -ENOMEM; |
@@ -933,7 +936,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info, | |||
933 | } | 936 | } |
934 | #endif | 937 | #endif |
935 | 938 | ||
936 | err = fib6_add_rt2node(fn, rt, info, mx, mx_len); | 939 | err = fib6_add_rt2node(fn, rt, info, mxc); |
937 | if (!err) { | 940 | if (!err) { |
938 | fib6_start_gc(info->nl_net, rt); | 941 | fib6_start_gc(info->nl_net, rt); |
939 | if (!(rt->rt6i_flags & RTF_CACHE)) | 942 | if (!(rt->rt6i_flags & RTF_CACHE)) |
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c91083156edb..454771d20b21 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -853,14 +853,14 @@ EXPORT_SYMBOL(rt6_lookup); | |||
853 | */ | 853 | */ |
854 | 854 | ||
855 | static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, | 855 | static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, |
856 | struct nlattr *mx, int mx_len) | 856 | struct mx6_config *mxc) |
857 | { | 857 | { |
858 | int err; | 858 | int err; |
859 | struct fib6_table *table; | 859 | struct fib6_table *table; |
860 | 860 | ||
861 | table = rt->rt6i_table; | 861 | table = rt->rt6i_table; |
862 | write_lock_bh(&table->tb6_lock); | 862 | write_lock_bh(&table->tb6_lock); |
863 | err = fib6_add(&table->tb6_root, rt, info, mx, mx_len); | 863 | err = fib6_add(&table->tb6_root, rt, info, mxc); |
864 | write_unlock_bh(&table->tb6_lock); | 864 | write_unlock_bh(&table->tb6_lock); |
865 | 865 | ||
866 | return err; | 866 | return err; |
@@ -868,10 +868,10 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, | |||
868 | 868 | ||
869 | int ip6_ins_rt(struct rt6_info *rt) | 869 | int ip6_ins_rt(struct rt6_info *rt) |
870 | { | 870 | { |
871 | struct nl_info info = { | 871 | struct nl_info info = { .nl_net = dev_net(rt->dst.dev), }; |
872 | .nl_net = dev_net(rt->dst.dev), | 872 | struct mx6_config mxc = { .mx = NULL, }; |
873 | }; | 873 | |
874 | return __ip6_ins_rt(rt, &info, NULL, 0); | 874 | return __ip6_ins_rt(rt, &info, &mxc); |
875 | } | 875 | } |
876 | 876 | ||
877 | static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, | 877 | static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, |
@@ -1470,9 +1470,39 @@ out: | |||
1470 | return entries > rt_max_size; | 1470 | return entries > rt_max_size; |
1471 | } | 1471 | } |
1472 | 1472 | ||
1473 | /* | 1473 | static int ip6_convert_metrics(struct mx6_config *mxc, |
1474 | * | 1474 | const struct fib6_config *cfg) |
1475 | */ | 1475 | { |
1476 | struct nlattr *nla; | ||
1477 | int remaining; | ||
1478 | u32 *mp; | ||
1479 | |||
1480 | if (cfg->fc_mx == NULL) | ||
1481 | return 0; | ||
1482 | |||
1483 | mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); | ||
1484 | if (unlikely(!mp)) | ||
1485 | return -ENOMEM; | ||
1486 | |||
1487 | nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { | ||
1488 | int type = nla_type(nla); | ||
1489 | |||
1490 | if (type) { | ||
1491 | if (unlikely(type > RTAX_MAX)) | ||
1492 | goto err; | ||
1493 | |||
1494 | mp[type - 1] = nla_get_u32(nla); | ||
1495 | __set_bit(type - 1, mxc->mx_valid); | ||
1496 | } | ||
1497 | } | ||
1498 | |||
1499 | mxc->mx = mp; | ||
1500 | |||
1501 | return 0; | ||
1502 | err: | ||
1503 | kfree(mp); | ||
1504 | return -EINVAL; | ||
1505 | } | ||
1476 | 1506 | ||
1477 | int ip6_route_add(struct fib6_config *cfg) | 1507 | int ip6_route_add(struct fib6_config *cfg) |
1478 | { | 1508 | { |
@@ -1482,6 +1512,7 @@ int ip6_route_add(struct fib6_config *cfg) | |||
1482 | struct net_device *dev = NULL; | 1512 | struct net_device *dev = NULL; |
1483 | struct inet6_dev *idev = NULL; | 1513 | struct inet6_dev *idev = NULL; |
1484 | struct fib6_table *table; | 1514 | struct fib6_table *table; |
1515 | struct mx6_config mxc = { .mx = NULL, }; | ||
1485 | int addr_type; | 1516 | int addr_type; |
1486 | 1517 | ||
1487 | if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) | 1518 | if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) |
@@ -1677,8 +1708,14 @@ install_route: | |||
1677 | 1708 | ||
1678 | cfg->fc_nlinfo.nl_net = dev_net(dev); | 1709 | cfg->fc_nlinfo.nl_net = dev_net(dev); |
1679 | 1710 | ||
1680 | return __ip6_ins_rt(rt, &cfg->fc_nlinfo, cfg->fc_mx, cfg->fc_mx_len); | 1711 | err = ip6_convert_metrics(&mxc, cfg); |
1712 | if (err) | ||
1713 | goto out; | ||
1714 | |||
1715 | err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); | ||
1681 | 1716 | ||
1717 | kfree(mxc.mx); | ||
1718 | return err; | ||
1682 | out: | 1719 | out: |
1683 | if (dev) | 1720 | if (dev) |
1684 | dev_put(dev); | 1721 | dev_put(dev); |