OSDN Git Service

net: rtnetlink: add linkprop commands to add and delete alternative ifnames
authorJiri Pirko <jiri@mellanox.com>
Mon, 30 Sep 2019 09:48:16 +0000 (11:48 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 1 Oct 2019 21:47:19 +0000 (14:47 -0700)
Add two commands to add and delete list of link properties. Implement
the first property type along - alternative ifnames.
Each net device can have multiple alternative names.

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netdevice.h
include/uapi/linux/if.h
include/uapi/linux/if_link.h
include/uapi/linux/rtnetlink.h
net/core/dev.c
net/core/rtnetlink.c
security/selinux/nlmsgtab.c

index e92bc54..48cc71a 100644 (file)
@@ -927,10 +927,14 @@ struct tlsdev_ops;
 
 struct netdev_name_node {
        struct hlist_node hlist;
+       struct list_head list;
        struct net_device *dev;
        const char *name;
 };
 
+int netdev_name_node_alt_create(struct net_device *dev, const char *name);
+int netdev_name_node_alt_destroy(struct net_device *dev, const char *name);
+
 /*
  * This structure defines the management hooks for network devices.
  * The following hooks can be defined; unless noted otherwise, they are
index 7fea0fd..4bf3334 100644 (file)
@@ -33,6 +33,7 @@
 #define        IFNAMSIZ        16
 #endif /* __UAPI_DEF_IF_IFNAMSIZ */
 #define        IFALIASZ        256
+#define        ALTIFNAMSIZ     128
 #include <linux/hdlc/ioctl.h>
 
 /* For glibc compatibility. An empty enum does not compile. */
index 4a8c02c..8aec876 100644 (file)
@@ -167,6 +167,8 @@ enum {
        IFLA_NEW_IFINDEX,
        IFLA_MIN_MTU,
        IFLA_MAX_MTU,
+       IFLA_PROP_LIST,
+       IFLA_ALT_IFNAME, /* Alternative ifname */
        __IFLA_MAX
 };
 
index ce2a623..1418a83 100644 (file)
@@ -164,6 +164,13 @@ enum {
        RTM_GETNEXTHOP,
 #define RTM_GETNEXTHOP RTM_GETNEXTHOP
 
+       RTM_NEWLINKPROP = 108,
+#define RTM_NEWLINKPROP        RTM_NEWLINKPROP
+       RTM_DELLINKPROP,
+#define RTM_DELLINKPROP        RTM_DELLINKPROP
+       RTM_GETLINKPROP,
+#define RTM_GETLINKPROP        RTM_GETLINKPROP
+
        __RTM_MAX,
 #define RTM_MAX                (((__RTM_MAX + 3) & ~3) - 1)
 };
index d2053d0..7a456c6 100644 (file)
@@ -245,7 +245,13 @@ static struct netdev_name_node *netdev_name_node_alloc(struct net_device *dev,
 static struct netdev_name_node *
 netdev_name_node_head_alloc(struct net_device *dev)
 {
-       return netdev_name_node_alloc(dev, dev->name);
+       struct netdev_name_node *name_node;
+
+       name_node = netdev_name_node_alloc(dev, dev->name);
+       if (!name_node)
+               return NULL;
+       INIT_LIST_HEAD(&name_node->list);
+       return name_node;
 }
 
 static void netdev_name_node_free(struct netdev_name_node *name_node)
@@ -289,6 +295,55 @@ static struct netdev_name_node *netdev_name_node_lookup_rcu(struct net *net,
        return NULL;
 }
 
+int netdev_name_node_alt_create(struct net_device *dev, const char *name)
+{
+       struct netdev_name_node *name_node;
+       struct net *net = dev_net(dev);
+
+       name_node = netdev_name_node_lookup(net, name);
+       if (name_node)
+               return -EEXIST;
+       name_node = netdev_name_node_alloc(dev, name);
+       if (!name_node)
+               return -ENOMEM;
+       netdev_name_node_add(net, name_node);
+       /* The node that holds dev->name acts as a head of per-device list. */
+       list_add_tail(&name_node->list, &dev->name_node->list);
+
+       return 0;
+}
+EXPORT_SYMBOL(netdev_name_node_alt_create);
+
+static void __netdev_name_node_alt_destroy(struct netdev_name_node *name_node)
+{
+       list_del(&name_node->list);
+       netdev_name_node_del(name_node);
+       kfree(name_node->name);
+       netdev_name_node_free(name_node);
+}
+
+int netdev_name_node_alt_destroy(struct net_device *dev, const char *name)
+{
+       struct netdev_name_node *name_node;
+       struct net *net = dev_net(dev);
+
+       name_node = netdev_name_node_lookup(net, name);
+       if (!name_node)
+               return -ENOENT;
+       __netdev_name_node_alt_destroy(name_node);
+
+       return 0;
+}
+EXPORT_SYMBOL(netdev_name_node_alt_destroy);
+
+static void netdev_name_node_alt_flush(struct net_device *dev)
+{
+       struct netdev_name_node *name_node, *tmp;
+
+       list_for_each_entry_safe(name_node, tmp, &dev->name_node->list, list)
+               __netdev_name_node_alt_destroy(name_node);
+}
+
 /* Device list insertion */
 static void list_netdevice(struct net_device *dev)
 {
@@ -8317,6 +8372,7 @@ static void rollback_registered_many(struct list_head *head)
                dev_uc_flush(dev);
                dev_mc_flush(dev);
 
+               netdev_name_node_alt_flush(dev);
                netdev_name_node_free(dev->name_node);
 
                if (dev->netdev_ops->ndo_uninit)
index 1ee6460..e136469 100644 (file)
@@ -1750,6 +1750,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] = {
@@ -3373,6 +3376,103 @@ 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]) {
+               char ifname[IFNAMSIZ];
+
+               nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
+               dev = __dev_get_by_name(net, ifname);
+       } 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);
@@ -5331,6 +5431,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);
index 58345ba..c97fdae 100644 (file)
@@ -83,6 +83,8 @@ static const struct nlmsg_perm nlmsg_route_perms[] =
        { RTM_NEWNEXTHOP,       NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
        { RTM_DELNEXTHOP,       NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
        { RTM_GETNEXTHOP,       NETLINK_ROUTE_SOCKET__NLMSG_READ  },
+       { RTM_NEWLINKPROP,      NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+       { RTM_DELLINKPROP,      NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
 };
 
 static const struct nlmsg_perm nlmsg_tcpdiag_perms[] =
@@ -166,7 +168,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
                 * structures at the top of this file with the new mappings
                 * before updating the BUILD_BUG_ON() macro!
                 */
-               BUILD_BUG_ON(RTM_MAX != (RTM_NEWNEXTHOP + 3));
+               BUILD_BUG_ON(RTM_MAX != (RTM_NEWLINKPROP + 3));
                err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
                                 sizeof(nlmsg_route_perms));
                break;