aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/ip6_fib.c47
-rw-r--r--net/ipv6/route.c44
2 files changed, 55 insertions, 36 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 075602fc6b6a..4ee487b103ae 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -638,12 +638,41 @@ static inline bool rt6_qualify_for_ecmp(struct rt6_info *rt)
638 RTF_GATEWAY; 638 RTF_GATEWAY;
639} 639}
640 640
641static int fib6_commit_metrics(struct dst_entry *dst,
642 struct nlattr *mx, int mx_len)
643{
644 struct nlattr *nla;
645 int remaining;
646 u32 *mp;
647
648 if (dst->flags & DST_HOST) {
649 mp = dst_metrics_write_ptr(dst);
650 } else {
651 mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
652 if (!mp)
653 return -ENOMEM;
654 dst_init_metrics(dst, mp, 0);
655 }
656
657 nla_for_each_attr(nla, mx, mx_len, remaining) {
658 int type = nla_type(nla);
659
660 if (type) {
661 if (type > RTAX_MAX)
662 return -EINVAL;
663
664 mp[type - 1] = nla_get_u32(nla);
665 }
666 }
667 return 0;
668}
669
641/* 670/*
642 * Insert routing information in a node. 671 * Insert routing information in a node.
643 */ 672 */
644 673
645static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, 674static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
646 struct nl_info *info) 675 struct nl_info *info, struct nlattr *mx, int mx_len)
647{ 676{
648 struct rt6_info *iter = NULL; 677 struct rt6_info *iter = NULL;
649 struct rt6_info **ins; 678 struct rt6_info **ins;
@@ -653,6 +682,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
653 (info->nlh->nlmsg_flags & NLM_F_CREATE)); 682 (info->nlh->nlmsg_flags & NLM_F_CREATE));
654 int found = 0; 683 int found = 0;
655 bool rt_can_ecmp = rt6_qualify_for_ecmp(rt); 684 bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
685 int err;
656 686
657 ins = &fn->leaf; 687 ins = &fn->leaf;
658 688
@@ -751,6 +781,11 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
751 pr_warn("NLM_F_CREATE should be set when creating new route\n"); 781 pr_warn("NLM_F_CREATE should be set when creating new route\n");
752 782
753add: 783add:
784 if (mx) {
785 err = fib6_commit_metrics(&rt->dst, mx, mx_len);
786 if (err)
787 return err;
788 }
754 rt->dst.rt6_next = iter; 789 rt->dst.rt6_next = iter;
755 *ins = rt; 790 *ins = rt;
756 rt->rt6i_node = fn; 791 rt->rt6i_node = fn;
@@ -770,6 +805,11 @@ add:
770 pr_warn("NLM_F_REPLACE set, but no existing node found!\n"); 805 pr_warn("NLM_F_REPLACE set, but no existing node found!\n");
771 return -ENOENT; 806 return -ENOENT;
772 } 807 }
808 if (mx) {
809 err = fib6_commit_metrics(&rt->dst, mx, mx_len);
810 if (err)
811 return err;
812 }
773 *ins = rt; 813 *ins = rt;
774 rt->rt6i_node = fn; 814 rt->rt6i_node = fn;
775 rt->dst.rt6_next = iter->dst.rt6_next; 815 rt->dst.rt6_next = iter->dst.rt6_next;
@@ -806,7 +846,8 @@ void fib6_force_start_gc(struct net *net)
806 * with source addr info in sub-trees 846 * with source addr info in sub-trees
807 */ 847 */
808 848
809int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) 849int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info,
850 struct nlattr *mx, int mx_len)
810{ 851{
811 struct fib6_node *fn, *pn = NULL; 852 struct fib6_node *fn, *pn = NULL;
812 int err = -ENOMEM; 853 int err = -ENOMEM;
@@ -900,7 +941,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
900 } 941 }
901#endif 942#endif
902 943
903 err = fib6_add_rt2node(fn, rt, info); 944 err = fib6_add_rt2node(fn, rt, info, mx, mx_len);
904 if (!err) { 945 if (!err) {
905 fib6_start_gc(info->nl_net, rt); 946 fib6_start_gc(info->nl_net, rt);
906 if (!(rt->rt6i_flags & RTF_CACHE)) 947 if (!(rt->rt6i_flags & RTF_CACHE))
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index fba54a407bb2..b93ae6a6a31c 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -149,7 +149,8 @@ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
149 unsigned long prev, new; 149 unsigned long prev, new;
150 150
151 p = peer->metrics; 151 p = peer->metrics;
152 if (inet_metrics_new(peer)) 152 if (inet_metrics_new(peer) ||
153 (old & DST_METRICS_FORCE_OVERWRITE))
153 memcpy(p, old_p, sizeof(u32) * RTAX_MAX); 154 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
154 155
155 new = (unsigned long) p; 156 new = (unsigned long) p;
@@ -857,14 +858,15 @@ EXPORT_SYMBOL(rt6_lookup);
857 be destroyed. 858 be destroyed.
858 */ 859 */
859 860
860static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info) 861static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
862 struct nlattr *mx, int mx_len)
861{ 863{
862 int err; 864 int err;
863 struct fib6_table *table; 865 struct fib6_table *table;
864 866
865 table = rt->rt6i_table; 867 table = rt->rt6i_table;
866 write_lock_bh(&table->tb6_lock); 868 write_lock_bh(&table->tb6_lock);
867 err = fib6_add(&table->tb6_root, rt, info); 869 err = fib6_add(&table->tb6_root, rt, info, mx, mx_len);
868 write_unlock_bh(&table->tb6_lock); 870 write_unlock_bh(&table->tb6_lock);
869 871
870 return err; 872 return err;
@@ -875,7 +877,7 @@ int ip6_ins_rt(struct rt6_info *rt)
875 struct nl_info info = { 877 struct nl_info info = {
876 .nl_net = dev_net(rt->dst.dev), 878 .nl_net = dev_net(rt->dst.dev),
877 }; 879 };
878 return __ip6_ins_rt(rt, &info); 880 return __ip6_ins_rt(rt, &info, NULL, 0);
879} 881}
880 882
881static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, 883static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
@@ -1543,17 +1545,11 @@ int ip6_route_add(struct fib6_config *cfg)
1543 1545
1544 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 1546 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1545 rt->rt6i_dst.plen = cfg->fc_dst_len; 1547 rt->rt6i_dst.plen = cfg->fc_dst_len;
1546 if (rt->rt6i_dst.plen == 128) 1548 if (rt->rt6i_dst.plen == 128) {
1547 rt->dst.flags |= DST_HOST; 1549 rt->dst.flags |= DST_HOST;
1548 1550 dst_metrics_set_force_overwrite(&rt->dst);
1549 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1550 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1551 if (!metrics) {
1552 err = -ENOMEM;
1553 goto out;
1554 }
1555 dst_init_metrics(&rt->dst, metrics, 0);
1556 } 1551 }
1552
1557#ifdef CONFIG_IPV6_SUBTREES 1553#ifdef CONFIG_IPV6_SUBTREES
1558 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 1554 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1559 rt->rt6i_src.plen = cfg->fc_src_len; 1555 rt->rt6i_src.plen = cfg->fc_src_len;
@@ -1672,31 +1668,13 @@ int ip6_route_add(struct fib6_config *cfg)
1672 rt->rt6i_flags = cfg->fc_flags; 1668 rt->rt6i_flags = cfg->fc_flags;
1673 1669
1674install_route: 1670install_route:
1675 if (cfg->fc_mx) {
1676 struct nlattr *nla;
1677 int remaining;
1678
1679 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1680 int type = nla_type(nla);
1681
1682 if (type) {
1683 if (type > RTAX_MAX) {
1684 err = -EINVAL;
1685 goto out;
1686 }
1687
1688 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
1689 }
1690 }
1691 }
1692
1693 rt->dst.dev = dev; 1671 rt->dst.dev = dev;
1694 rt->rt6i_idev = idev; 1672 rt->rt6i_idev = idev;
1695 rt->rt6i_table = table; 1673 rt->rt6i_table = table;
1696 1674
1697 cfg->fc_nlinfo.nl_net = dev_net(dev); 1675 cfg->fc_nlinfo.nl_net = dev_net(dev);
1698 1676
1699 return __ip6_ins_rt(rt, &cfg->fc_nlinfo); 1677 return __ip6_ins_rt(rt, &cfg->fc_nlinfo, cfg->fc_mx, cfg->fc_mx_len);
1700 1678
1701out: 1679out:
1702 if (dev) 1680 if (dev)