Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-2.6-block.git] / net / ipv6 / route.c
index c91083156edbe2b631b1fb11e3ead35f1925b8a7..7622951e5fbb569cd80a645db80286092815be6f 100644 (file)
@@ -853,14 +853,14 @@ EXPORT_SYMBOL(rt6_lookup);
  */
 
 static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
-                       struct nlattr *mx, int mx_len)
+                       struct mx6_config *mxc)
 {
        int err;
        struct fib6_table *table;
 
        table = rt->rt6i_table;
        write_lock_bh(&table->tb6_lock);
-       err = fib6_add(&table->tb6_root, rt, info, mx, mx_len);
+       err = fib6_add(&table->tb6_root, rt, info, mxc);
        write_unlock_bh(&table->tb6_lock);
 
        return err;
@@ -868,10 +868,10 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
 
 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, NULL, 0);
+       struct nl_info info = { .nl_net = dev_net(rt->dst.dev), };
+       struct mx6_config mxc = { .mx = NULL, };
+
+       return __ip6_ins_rt(rt, &info, &mxc);
 }
 
 static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
@@ -1160,12 +1160,9 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
                struct net *net = dev_net(dst->dev);
 
                rt6->rt6i_flags |= RTF_MODIFIED;
-               if (mtu < IPV6_MIN_MTU) {
-                       u32 features = dst_metric(dst, RTAX_FEATURES);
+               if (mtu < IPV6_MIN_MTU)
                        mtu = IPV6_MIN_MTU;
-                       features |= RTAX_FEATURE_ALLFRAG;
-                       dst_metric_set(dst, RTAX_FEATURES, features);
-               }
+
                dst_metric_set(dst, RTAX_MTU, mtu);
                rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
        }
@@ -1245,12 +1242,16 @@ restart:
                rt = net->ipv6.ip6_null_entry;
        else if (rt->dst.error) {
                rt = net->ipv6.ip6_null_entry;
-       } else if (rt == net->ipv6.ip6_null_entry) {
+               goto out;
+       }
+
+       if (rt == net->ipv6.ip6_null_entry) {
                fn = fib6_backtrack(fn, &fl6->saddr);
                if (fn)
                        goto restart;
        }
 
+out:
        dst_hold(&rt->dst);
 
        read_unlock_bh(&table->tb6_lock);
@@ -1470,9 +1471,51 @@ out:
        return entries > rt_max_size;
 }
 
-/*
- *
- */
+static int ip6_convert_metrics(struct mx6_config *mxc,
+                              const struct fib6_config *cfg)
+{
+       struct nlattr *nla;
+       int remaining;
+       u32 *mp;
+
+       if (cfg->fc_mx == NULL)
+               return 0;
+
+       mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
+       if (unlikely(!mp))
+               return -ENOMEM;
+
+       nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
+               int type = nla_type(nla);
+
+               if (type) {
+                       u32 val;
+
+                       if (unlikely(type > RTAX_MAX))
+                               goto err;
+                       if (type == RTAX_CC_ALGO) {
+                               char tmp[TCP_CA_NAME_MAX];
+
+                               nla_strlcpy(tmp, nla, sizeof(tmp));
+                               val = tcp_ca_get_key_by_name(tmp);
+                               if (val == TCP_CA_UNSPEC)
+                                       goto err;
+                       } else {
+                               val = nla_get_u32(nla);
+                       }
+
+                       mp[type - 1] = val;
+                       __set_bit(type - 1, mxc->mx_valid);
+               }
+       }
+
+       mxc->mx = mp;
+
+       return 0;
+ err:
+       kfree(mp);
+       return -EINVAL;
+}
 
 int ip6_route_add(struct fib6_config *cfg)
 {
@@ -1482,6 +1525,7 @@ int ip6_route_add(struct fib6_config *cfg)
        struct net_device *dev = NULL;
        struct inet6_dev *idev = NULL;
        struct fib6_table *table;
+       struct mx6_config mxc = { .mx = NULL, };
        int addr_type;
 
        if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
@@ -1677,8 +1721,14 @@ install_route:
 
        cfg->fc_nlinfo.nl_net = dev_net(dev);
 
-       return __ip6_ins_rt(rt, &cfg->fc_nlinfo, cfg->fc_mx, cfg->fc_mx_len);
+       err = ip6_convert_metrics(&mxc, cfg);
+       if (err)
+               goto out;
+
+       err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
 
+       kfree(mxc.mx);
+       return err;
 out:
        if (dev)
                dev_put(dev);
@@ -2534,7 +2584,8 @@ static inline size_t rt6_nlmsg_size(void)
               + nla_total_size(4) /* RTA_OIF */
               + nla_total_size(4) /* RTA_PRIORITY */
               + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
-              + nla_total_size(sizeof(struct rta_cacheinfo));
+              + nla_total_size(sizeof(struct rta_cacheinfo))
+              + nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */
 }
 
 static int rt6_fill_node(struct net *net,
@@ -2675,7 +2726,8 @@ static int rt6_fill_node(struct net *net,
        if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
                goto nla_put_failure;
 
-       return nlmsg_end(skb, nlh);
+       nlmsg_end(skb, nlh);
+       return 0;
 
 nla_put_failure:
        nlmsg_cancel(skb, nlh);