rtnl: fix error path when adding an iface with a link net
[cascardo/linux.git] / net / core / rtnetlink.c
index d06107d..07447d1 100644 (file)
@@ -50,6 +50,7 @@
 #include <net/arp.h>
 #include <net/route.h>
 #include <net/udp.h>
+#include <net/tcp.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 #include <net/fib_rules.h>
@@ -669,9 +670,19 @@ int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics)
 
        for (i = 0; i < RTAX_MAX; i++) {
                if (metrics[i]) {
+                       if (i == RTAX_CC_ALGO - 1) {
+                               char tmp[TCP_CA_NAME_MAX], *name;
+
+                               name = tcp_ca_get_name_by_key(metrics[i], tmp);
+                               if (!name)
+                                       continue;
+                               if (nla_put_string(skb, i + 1, name))
+                                       goto nla_put_failure;
+                       } else {
+                               if (nla_put_u32(skb, i + 1, metrics[i]))
+                                       goto nla_put_failure;
+                       }
                        valid++;
-                       if (nla_put_u32(skb, i+1, metrics[i]))
-                               goto nla_put_failure;
                }
        }
 
@@ -864,6 +875,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
               + nla_total_size(1) /* IFLA_OPERSTATE */
               + nla_total_size(1) /* IFLA_LINKMODE */
               + nla_total_size(4) /* IFLA_CARRIER_CHANGES */
+              + nla_total_size(4) /* IFLA_LINK_NETNSID */
               + nla_total_size(ext_filter_mask
                                & RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */
               + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */
@@ -1158,6 +1170,18 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        goto nla_put_failure;
        }
 
+       if (dev->rtnl_link_ops &&
+           dev->rtnl_link_ops->get_link_net) {
+               struct net *link_net = dev->rtnl_link_ops->get_link_net(dev);
+
+               if (!net_eq(dev_net(dev), link_net)) {
+                       int id = peernet2id(dev_net(dev), link_net);
+
+                       if (nla_put_s32(skb, IFLA_LINK_NETNSID, id))
+                               goto nla_put_failure;
+               }
+       }
+
        if (!(af_spec = nla_nest_start(skb, IFLA_AF_SPEC)))
                goto nla_put_failure;
 
@@ -1188,7 +1212,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
 
        nla_nest_end(skb, af_spec);
 
-       return nlmsg_end(skb, nlh);
+       nlmsg_end(skb, nlh);
+       return 0;
 
 nla_put_failure:
        nlmsg_cancel(skb, nlh);
@@ -1223,6 +1248,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
        [IFLA_PHYS_PORT_ID]     = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN },
        [IFLA_CARRIER_CHANGES]  = { .type = NLA_U32 },  /* ignored */
        [IFLA_PHYS_SWITCH_ID]   = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN },
+       [IFLA_LINK_NETNSID]     = { .type = NLA_S32 },
 };
 
 static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
@@ -1315,7 +1341,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
                         */
                        WARN_ON((err == -EMSGSIZE) && (skb->len == 0));
 
-                       if (err <= 0)
+                       if (err < 0)
                                goto out;
 
                        nl_dump_check_consistent(cb, nlmsg_hdr(skb));
@@ -1996,7 +2022,7 @@ replay:
                struct nlattr *slave_attr[m_ops ? m_ops->slave_maxtype + 1 : 0];
                struct nlattr **data = NULL;
                struct nlattr **slave_data = NULL;
-               struct net *dest_net;
+               struct net *dest_net, *link_net = NULL;
 
                if (ops) {
                        if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
@@ -2102,7 +2128,18 @@ replay:
                if (IS_ERR(dest_net))
                        return PTR_ERR(dest_net);
 
-               dev = rtnl_create_link(dest_net, ifname, name_assign_type, ops, tb);
+               if (tb[IFLA_LINK_NETNSID]) {
+                       int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
+
+                       link_net = get_net_ns_by_id(dest_net, id);
+                       if (!link_net) {
+                               err =  -EINVAL;
+                               goto out;
+                       }
+               }
+
+               dev = rtnl_create_link(link_net ? : dest_net, ifname,
+                                      name_assign_type, ops, tb);
                if (IS_ERR(dev)) {
                        err = PTR_ERR(dev);
                        goto out;
@@ -2130,9 +2167,19 @@ replay:
                        }
                }
                err = rtnl_configure_link(dev, ifm);
-               if (err < 0)
+               if (err < 0) {
                        unregister_netdevice(dev);
+                       goto out;
+               }
+
+               if (link_net) {
+                       err = dev_change_net_namespace(dev, dest_net, ifname);
+                       if (err < 0)
+                               unregister_netdevice(dev);
+               }
 out:
+               if (link_net)
+                       put_net(link_net);
                put_net(dest_net);
                return err;
        }
@@ -2315,7 +2362,8 @@ static int nlmsg_populate_fdb_fill(struct sk_buff *skb,
        if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr))
                goto nla_put_failure;
 
-       return nlmsg_end(skb, nlh);
+       nlmsg_end(skb, nlh);
+       return 0;
 
 nla_put_failure:
        nlmsg_cancel(skb, nlh);
@@ -2368,6 +2416,11 @@ int ndo_dflt_fdb_add(struct ndmsg *ndm,
                return err;
        }
 
+       if (vid) {
+               pr_info("%s: vlans aren't supported yet for dev_uc|mc_add()\n", dev->name);
+               return err;
+       }
+
        if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
                err = dev_uc_add_excl(dev, addr);
        else if (is_multicast_ether_addr(addr))
@@ -2693,10 +2746,11 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
                                                         idx);
                }
 
-               idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx);
                if (dev->netdev_ops->ndo_fdb_dump)
-                       idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, bdev, dev,
+                       idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, NULL,
                                                            idx);
+               else
+                       idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx);
 
                cops = NULL;
        }
@@ -2792,7 +2846,8 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
 
        nla_nest_end(skb, protinfo);
 
-       return nlmsg_end(skb, nlh);
+       nlmsg_end(skb, nlh);
+       return 0;
 nla_put_failure:
        nlmsg_cancel(skb, nlh);
        return -EMSGSIZE;
@@ -2863,32 +2918,24 @@ static inline size_t bridge_nlmsg_size(void)
                + nla_total_size(sizeof(u16));  /* IFLA_BRIDGE_MODE */
 }
 
-static int rtnl_bridge_notify(struct net_device *dev, u16 flags)
+static int rtnl_bridge_notify(struct net_device *dev)
 {
        struct net *net = dev_net(dev);
-       struct net_device *br_dev = netdev_master_upper_dev_get(dev);
        struct sk_buff *skb;
        int err = -EOPNOTSUPP;
 
+       if (!dev->netdev_ops->ndo_bridge_getlink)
+               return 0;
+
        skb = nlmsg_new(bridge_nlmsg_size(), GFP_ATOMIC);
        if (!skb) {
                err = -ENOMEM;
                goto errout;
        }
 
-       if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) &&
-           br_dev && br_dev->netdev_ops->ndo_bridge_getlink) {
-               err = br_dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0);
-               if (err < 0)
-                       goto errout;
-       }
-
-       if ((flags & BRIDGE_FLAGS_SELF) &&
-           dev->netdev_ops->ndo_bridge_getlink) {
-               err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0);
-               if (err < 0)
-                       goto errout;
-       }
+       err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0);
+       if (err < 0)
+               goto errout;
 
        rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
        return 0;
@@ -2906,7 +2953,7 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct net_device *dev;
        struct nlattr *br_spec, *attr = NULL;
        int rem, err = -EOPNOTSUPP;
-       u16 oflags, flags = 0;
+       u16 flags = 0;
        bool have_flags = false;
 
        if (nlmsg_len(nlh) < sizeof(*ifm))
@@ -2936,8 +2983,6 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
                }
        }
 
-       oflags = flags;
-
        if (!flags || (flags & BRIDGE_FLAGS_MASTER)) {
                struct net_device *br_dev = netdev_master_upper_dev_get(dev);
 
@@ -2958,16 +3003,18 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
                        err = -EOPNOTSUPP;
                else
                        err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh);
-
-               if (!err)
+               if (!err) {
                        flags &= ~BRIDGE_FLAGS_SELF;
+
+                       /* Generate event to notify upper layer of bridge
+                        * change
+                        */
+                       err = rtnl_bridge_notify(dev);
+               }
        }
 
        if (have_flags)
                memcpy(nla_data(attr), &flags, sizeof(flags));
-       /* Generate event to notify upper layer of bridge change */
-       if (!err)
-               err = rtnl_bridge_notify(dev, oflags);
 out:
        return err;
 }
@@ -2979,7 +3026,7 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct net_device *dev;
        struct nlattr *br_spec, *attr = NULL;
        int rem, err = -EOPNOTSUPP;
-       u16 oflags, flags = 0;
+       u16 flags = 0;
        bool have_flags = false;
 
        if (nlmsg_len(nlh) < sizeof(*ifm))
@@ -3009,8 +3056,6 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
                }
        }
 
-       oflags = flags;
-
        if (!flags || (flags & BRIDGE_FLAGS_MASTER)) {
                struct net_device *br_dev = netdev_master_upper_dev_get(dev);
 
@@ -3032,15 +3077,18 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
                else
                        err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh);
 
-               if (!err)
+               if (!err) {
                        flags &= ~BRIDGE_FLAGS_SELF;
+
+                       /* Generate event to notify upper layer of bridge
+                        * change
+                        */
+                       err = rtnl_bridge_notify(dev);
+               }
        }
 
        if (have_flags)
                memcpy(nla_data(attr), &flags, sizeof(flags));
-       /* Generate event to notify upper layer of bridge change */
-       if (!err)
-               err = rtnl_bridge_notify(dev, oflags);
 out:
        return err;
 }