ipv6: do not overwrite inetpeer metrics prematurely
[deliverable/linux.git] / net / ipv6 / route.c
index fba54a407bb2b7c2aae62ac2d03df806bc1a794a..b93ae6a6a31c2b3bb882f01180aa32c38df2c853 100644 (file)
@@ -149,7 +149,8 @@ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
                unsigned long prev, new;
 
                p = peer->metrics;
-               if (inet_metrics_new(peer))
+               if (inet_metrics_new(peer) ||
+                   (old & DST_METRICS_FORCE_OVERWRITE))
                        memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
 
                new = (unsigned long) p;
@@ -857,14 +858,15 @@ EXPORT_SYMBOL(rt6_lookup);
    be destroyed.
  */
 
-static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
+static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
+                       struct nlattr *mx, int mx_len)
 {
        int err;
        struct fib6_table *table;
 
        table = rt->rt6i_table;
        write_lock_bh(&table->tb6_lock);
-       err = fib6_add(&table->tb6_root, rt, info);
+       err = fib6_add(&table->tb6_root, rt, info, mx, mx_len);
        write_unlock_bh(&table->tb6_lock);
 
        return err;
@@ -875,7 +877,7 @@ int ip6_ins_rt(struct rt6_info *rt)
        struct nl_info info = {
                .nl_net = dev_net(rt->dst.dev),
        };
-       return __ip6_ins_rt(rt, &info);
+       return __ip6_ins_rt(rt, &info, NULL, 0);
 }
 
 static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
@@ -1543,17 +1545,11 @@ int ip6_route_add(struct fib6_config *cfg)
 
        ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
        rt->rt6i_dst.plen = cfg->fc_dst_len;
-       if (rt->rt6i_dst.plen == 128)
-              rt->dst.flags |= DST_HOST;
-
-       if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
-               u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
-               if (!metrics) {
-                       err = -ENOMEM;
-                       goto out;
-               }
-               dst_init_metrics(&rt->dst, metrics, 0);
+       if (rt->rt6i_dst.plen == 128) {
+               rt->dst.flags |= DST_HOST;
+               dst_metrics_set_force_overwrite(&rt->dst);
        }
+
 #ifdef CONFIG_IPV6_SUBTREES
        ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
        rt->rt6i_src.plen = cfg->fc_src_len;
@@ -1672,31 +1668,13 @@ int ip6_route_add(struct fib6_config *cfg)
        rt->rt6i_flags = cfg->fc_flags;
 
 install_route:
-       if (cfg->fc_mx) {
-               struct nlattr *nla;
-               int remaining;
-
-               nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
-                       int type = nla_type(nla);
-
-                       if (type) {
-                               if (type > RTAX_MAX) {
-                                       err = -EINVAL;
-                                       goto out;
-                               }
-
-                               dst_metric_set(&rt->dst, type, nla_get_u32(nla));
-                       }
-               }
-       }
-
        rt->dst.dev = dev;
        rt->rt6i_idev = idev;
        rt->rt6i_table = table;
 
        cfg->fc_nlinfo.nl_net = dev_net(dev);
 
-       return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
+       return __ip6_ins_rt(rt, &cfg->fc_nlinfo, cfg->fc_mx, cfg->fc_mx_len);
 
 out:
        if (dev)
This page took 0.064589 seconds and 5 git commands to generate.