aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2015-01-05 17:57:44 -0500
committerDavid S. Miller <davem@davemloft.net>2015-01-05 22:55:24 -0500
commite715b6d3a5ef55834778d49224e60e8ccb5bf45f (patch)
tree61b0e9a29119fbe6062956f9429711f21eb1cefb
parent0409c9a5a7546043d32e8c46df0dcaf65f97f659 (diff)
net: fib6: convert cfg metric to u32 outside of table write lock
Do the nla validation earlier, outside the write lock. This is needed by followup patch which needs to be able to call request_module (which can sleep) if needed. Joint work with Daniel Borkmann. Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: David S. Miller <davem@davemloft.net>
-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);