]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/ipv6/ip6_fib.c
ipv6: do not overwrite inetpeer metrics prematurely
[karo-tx-linux.git] / net / ipv6 / ip6_fib.c
index 075602fc6b6a915f15a7039cf1daaa640e02ce82..4ee487b103aefcaeb808f6def6620adfe729d576 100644 (file)
@@ -638,12 +638,41 @@ static inline bool rt6_qualify_for_ecmp(struct rt6_info *rt)
               RTF_GATEWAY;
 }
 
+static int fib6_commit_metrics(struct dst_entry *dst,
+                              struct nlattr *mx, int mx_len)
+{
+       struct nlattr *nla;
+       int remaining;
+       u32 *mp;
+
+       if (dst->flags & DST_HOST) {
+               mp = dst_metrics_write_ptr(dst);
+       } else {
+               mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
+               if (!mp)
+                       return -ENOMEM;
+               dst_init_metrics(dst, mp, 0);
+       }
+
+       nla_for_each_attr(nla, mx, mx_len, remaining) {
+               int type = nla_type(nla);
+
+               if (type) {
+                       if (type > RTAX_MAX)
+                               return -EINVAL;
+
+                       mp[type - 1] = nla_get_u32(nla);
+               }
+       }
+       return 0;
+}
+
 /*
  *     Insert routing information in a node.
  */
 
 static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
-                           struct nl_info *info)
+                           struct nl_info *info, struct nlattr *mx, int mx_len)
 {
        struct rt6_info *iter = NULL;
        struct rt6_info **ins;
@@ -653,6 +682,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
                   (info->nlh->nlmsg_flags & NLM_F_CREATE));
        int found = 0;
        bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
+       int err;
 
        ins = &fn->leaf;
 
@@ -751,6 +781,11 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
                        pr_warn("NLM_F_CREATE should be set when creating new route\n");
 
 add:
+               if (mx) {
+                       err = fib6_commit_metrics(&rt->dst, mx, mx_len);
+                       if (err)
+                               return err;
+               }
                rt->dst.rt6_next = iter;
                *ins = rt;
                rt->rt6i_node = fn;
@@ -770,6 +805,11 @@ add:
                        pr_warn("NLM_F_REPLACE set, but no existing node found!\n");
                        return -ENOENT;
                }
+               if (mx) {
+                       err = fib6_commit_metrics(&rt->dst, mx, mx_len);
+                       if (err)
+                               return err;
+               }
                *ins = rt;
                rt->rt6i_node = fn;
                rt->dst.rt6_next = iter->dst.rt6_next;
@@ -806,7 +846,8 @@ void fib6_force_start_gc(struct net *net)
  *     with source addr info in sub-trees
  */
 
-int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
+int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info,
+            struct nlattr *mx, int mx_len)
 {
        struct fib6_node *fn, *pn = NULL;
        int err = -ENOMEM;
@@ -900,7 +941,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
        }
 #endif
 
-       err = fib6_add_rt2node(fn, rt, info);
+       err = fib6_add_rt2node(fn, rt, info, mx, mx_len);
        if (!err) {
                fib6_start_gc(info->nl_net, rt);
                if (!(rt->rt6i_flags & RTF_CACHE))