aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/ip6_fib.h10
-rw-r--r--net/ipv6/ip6_fib.c69
-rw-r--r--net/ipv6/route.c57
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
77struct 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,
291void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), 296void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg),
292 void *arg); 297 void *arg);
293 298
294int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info, 299int 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
297int fib6_del(struct rt6_info *rt, struct nl_info *info); 301int fib6_del(struct rt6_info *rt, struct nl_info *info);
298 302
299void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info); 303void 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
633static int fib6_commit_metrics(struct dst_entry *dst, 633static 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
643static 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
665static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, 669static 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
774add: 778add:
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
840int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info, 843int 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
855static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, 855static 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
869int ip6_ins_rt(struct rt6_info *rt) 869int 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
877static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, 877static 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/* 1473static 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
1477int ip6_route_add(struct fib6_config *cfg) 1507int 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;
1682out: 1719out:
1683 if (dev) 1720 if (dev)
1684 dev_put(dev); 1721 dev_put(dev);