Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[linux-2.6-microblaze.git] / net / core / rtnetlink.c
index e4ec575..9f7aa44 100644 (file)
@@ -980,6 +980,19 @@ static size_t rtnl_xdp_size(void)
        return xdp_size;
 }
 
+static size_t rtnl_prop_list_size(const struct net_device *dev)
+{
+       struct netdev_name_node *name_node;
+       size_t size;
+
+       if (list_empty(&dev->name_node->list))
+               return 0;
+       size = nla_total_size(0);
+       list_for_each_entry(name_node, &dev->name_node->list, list)
+               size += nla_total_size(ALTIFNAMSIZ);
+       return size;
+}
+
 static noinline size_t if_nlmsg_size(const struct net_device *dev,
                                     u32 ext_filter_mask)
 {
@@ -1027,6 +1040,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
               + nla_total_size(4)  /* IFLA_CARRIER_DOWN_COUNT */
               + nla_total_size(4)  /* IFLA_MIN_MTU */
               + nla_total_size(4)  /* IFLA_MAX_MTU */
+              + rtnl_prop_list_size(dev)
               + 0;
 }
 
@@ -1584,6 +1598,42 @@ static int rtnl_fill_link_af(struct sk_buff *skb,
        return 0;
 }
 
+static int rtnl_fill_alt_ifnames(struct sk_buff *skb,
+                                const struct net_device *dev)
+{
+       struct netdev_name_node *name_node;
+       int count = 0;
+
+       list_for_each_entry(name_node, &dev->name_node->list, list) {
+               if (nla_put_string(skb, IFLA_ALT_IFNAME, name_node->name))
+                       return -EMSGSIZE;
+               count++;
+       }
+       return count;
+}
+
+static int rtnl_fill_prop_list(struct sk_buff *skb,
+                              const struct net_device *dev)
+{
+       struct nlattr *prop_list;
+       int ret;
+
+       prop_list = nla_nest_start(skb, IFLA_PROP_LIST);
+       if (!prop_list)
+               return -EMSGSIZE;
+
+       ret = rtnl_fill_alt_ifnames(skb, dev);
+       if (ret <= 0)
+               goto nest_cancel;
+
+       nla_nest_end(skb, prop_list);
+       return 0;
+
+nest_cancel:
+       nla_nest_cancel(skb, prop_list);
+       return ret;
+}
+
 static int rtnl_fill_ifinfo(struct sk_buff *skb,
                            struct net_device *dev, struct net *src_net,
                            int type, u32 pid, u32 seq, u32 change,
@@ -1697,6 +1747,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
                goto nla_put_failure_rcu;
        rcu_read_unlock();
 
+       if (rtnl_fill_prop_list(skb, dev))
+               goto nla_put_failure;
+
        nlmsg_end(skb, nlh);
        return 0;
 
@@ -1750,6 +1803,9 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
        [IFLA_CARRIER_DOWN_COUNT] = { .type = NLA_U32 },
        [IFLA_MIN_MTU]          = { .type = NLA_U32 },
        [IFLA_MAX_MTU]          = { .type = NLA_U32 },
+       [IFLA_PROP_LIST]        = { .type = NLA_NESTED },
+       [IFLA_ALT_IFNAME]       = { .type = NLA_STRING,
+                                   .len = ALTIFNAMSIZ - 1 },
 };
 
 static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
@@ -2744,6 +2800,26 @@ errout:
        return err;
 }
 
+static struct net_device *rtnl_dev_get(struct net *net,
+                                      struct nlattr *ifname_attr,
+                                      struct nlattr *altifname_attr,
+                                      char *ifname)
+{
+       char buffer[ALTIFNAMSIZ];
+
+       if (!ifname) {
+               ifname = buffer;
+               if (ifname_attr)
+                       nla_strlcpy(ifname, ifname_attr, IFNAMSIZ);
+               else if (altifname_attr)
+                       nla_strlcpy(ifname, altifname_attr, ALTIFNAMSIZ);
+               else
+                       return NULL;
+       }
+
+       return __dev_get_by_name(net, ifname);
+}
+
 static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
                        struct netlink_ext_ack *extack)
 {
@@ -2772,8 +2848,8 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
        ifm = nlmsg_data(nlh);
        if (ifm->ifi_index > 0)
                dev = __dev_get_by_index(net, ifm->ifi_index);
-       else if (tb[IFLA_IFNAME])
-               dev = __dev_get_by_name(net, ifname);
+       else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME])
+               dev = rtnl_dev_get(net, NULL, tb[IFLA_ALT_IFNAME], ifname);
        else
                goto errout;
 
@@ -2846,7 +2922,6 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct net *tgt_net = net;
        struct net_device *dev = NULL;
        struct ifinfomsg *ifm;
-       char ifname[IFNAMSIZ];
        struct nlattr *tb[IFLA_MAX+1];
        int err;
        int netnsid = -1;
@@ -2860,9 +2935,6 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (err < 0)
                return err;
 
-       if (tb[IFLA_IFNAME])
-               nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
-
        if (tb[IFLA_TARGET_NETNSID]) {
                netnsid = nla_get_s32(tb[IFLA_TARGET_NETNSID]);
                tgt_net = rtnl_get_net_ns_capable(NETLINK_CB(skb).sk, netnsid);
@@ -2874,8 +2946,9 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
        ifm = nlmsg_data(nlh);
        if (ifm->ifi_index > 0)
                dev = __dev_get_by_index(tgt_net, ifm->ifi_index);
-       else if (tb[IFLA_IFNAME])
-               dev = __dev_get_by_name(tgt_net, ifname);
+       else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME])
+               dev = rtnl_dev_get(net, tb[IFLA_IFNAME],
+                                  tb[IFLA_ALT_IFNAME], NULL);
        else if (tb[IFLA_GROUP])
                err = rtnl_group_dellink(tgt_net, nla_get_u32(tb[IFLA_GROUP]));
        else
@@ -3046,12 +3119,10 @@ replay:
        ifm = nlmsg_data(nlh);
        if (ifm->ifi_index > 0)
                dev = __dev_get_by_index(net, ifm->ifi_index);
-       else {
-               if (ifname[0])
-                       dev = __dev_get_by_name(net, ifname);
-               else
-                       dev = NULL;
-       }
+       else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME])
+               dev = rtnl_dev_get(net, NULL, tb[IFLA_ALT_IFNAME], ifname);
+       else
+               dev = NULL;
 
        if (dev) {
                master_dev = netdev_master_upper_dev_get(dev);
@@ -3313,6 +3384,7 @@ static int rtnl_valid_getlink_req(struct sk_buff *skb,
 
                switch (i) {
                case IFLA_IFNAME:
+               case IFLA_ALT_IFNAME:
                case IFLA_EXT_MASK:
                case IFLA_TARGET_NETNSID:
                        break;
@@ -3331,7 +3403,6 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct net *net = sock_net(skb->sk);
        struct net *tgt_net = net;
        struct ifinfomsg *ifm;
-       char ifname[IFNAMSIZ];
        struct nlattr *tb[IFLA_MAX+1];
        struct net_device *dev = NULL;
        struct sk_buff *nskb;
@@ -3354,9 +3425,6 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
                        return PTR_ERR(tgt_net);
        }
 
-       if (tb[IFLA_IFNAME])
-               nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
-
        if (tb[IFLA_EXT_MASK])
                ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
 
@@ -3364,8 +3432,9 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
        ifm = nlmsg_data(nlh);
        if (ifm->ifi_index > 0)
                dev = __dev_get_by_index(tgt_net, ifm->ifi_index);
-       else if (tb[IFLA_IFNAME])
-               dev = __dev_get_by_name(tgt_net, ifname);
+       else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME])
+               dev = rtnl_dev_get(tgt_net, tb[IFLA_IFNAME],
+                                  tb[IFLA_ALT_IFNAME], NULL);
        else
                goto out;
 
@@ -3395,6 +3464,100 @@ out:
        return err;
 }
 
+static int rtnl_alt_ifname(int cmd, struct net_device *dev, struct nlattr *attr,
+                          bool *changed, struct netlink_ext_ack *extack)
+{
+       char *alt_ifname;
+       int err;
+
+       err = nla_validate(attr, attr->nla_len, IFLA_MAX, ifla_policy, extack);
+       if (err)
+               return err;
+
+       alt_ifname = nla_data(attr);
+       if (cmd == RTM_NEWLINKPROP) {
+               alt_ifname = kstrdup(alt_ifname, GFP_KERNEL);
+               if (!alt_ifname)
+                       return -ENOMEM;
+               err = netdev_name_node_alt_create(dev, alt_ifname);
+               if (err) {
+                       kfree(alt_ifname);
+                       return err;
+               }
+       } else if (cmd == RTM_DELLINKPROP) {
+               err = netdev_name_node_alt_destroy(dev, alt_ifname);
+               if (err)
+                       return err;
+       } else {
+               WARN_ON(1);
+               return 0;
+       }
+
+       *changed = true;
+       return 0;
+}
+
+static int rtnl_linkprop(int cmd, struct sk_buff *skb, struct nlmsghdr *nlh,
+                        struct netlink_ext_ack *extack)
+{
+       struct net *net = sock_net(skb->sk);
+       struct nlattr *tb[IFLA_MAX + 1];
+       struct net_device *dev;
+       struct ifinfomsg *ifm;
+       bool changed = false;
+       struct nlattr *attr;
+       int err, rem;
+
+       err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, extack);
+       if (err)
+               return err;
+
+       err = rtnl_ensure_unique_netns(tb, extack, true);
+       if (err)
+               return err;
+
+       ifm = nlmsg_data(nlh);
+       if (ifm->ifi_index > 0)
+               dev = __dev_get_by_index(net, ifm->ifi_index);
+       else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME])
+               dev = rtnl_dev_get(net, tb[IFLA_IFNAME],
+                                  tb[IFLA_ALT_IFNAME], NULL);
+       else
+               return -EINVAL;
+
+       if (!dev)
+               return -ENODEV;
+
+       if (!tb[IFLA_PROP_LIST])
+               return 0;
+
+       nla_for_each_nested(attr, tb[IFLA_PROP_LIST], rem) {
+               switch (nla_type(attr)) {
+               case IFLA_ALT_IFNAME:
+                       err = rtnl_alt_ifname(cmd, dev, attr, &changed, extack);
+                       if (err)
+                               return err;
+                       break;
+               }
+       }
+
+       if (changed)
+               netdev_state_change(dev);
+       return 0;
+}
+
+static int rtnl_newlinkprop(struct sk_buff *skb, struct nlmsghdr *nlh,
+                           struct netlink_ext_ack *extack)
+{
+       return rtnl_linkprop(RTM_NEWLINKPROP, skb, nlh, extack);
+}
+
+static int rtnl_dellinkprop(struct sk_buff *skb, struct nlmsghdr *nlh,
+                           struct netlink_ext_ack *extack)
+{
+       return rtnl_linkprop(RTM_DELLINKPROP, skb, nlh, extack);
+}
+
 static u16 rtnl_calcit(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
@@ -5353,6 +5516,9 @@ void __init rtnetlink_init(void)
        rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, 0);
        rtnl_register(PF_UNSPEC, RTM_GETNETCONF, NULL, rtnl_dump_all, 0);
 
+       rtnl_register(PF_UNSPEC, RTM_NEWLINKPROP, rtnl_newlinkprop, NULL, 0);
+       rtnl_register(PF_UNSPEC, RTM_DELLINKPROP, rtnl_dellinkprop, NULL, 0);
+
        rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, 0);
        rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, 0);
        rtnl_register(PF_BRIDGE, RTM_GETNEIGH, rtnl_fdb_get, rtnl_fdb_dump, 0);