aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ip6_fib.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r--net/ipv6/ip6_fib.c47
1 files changed, 44 insertions, 3 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))