bridge: allow creating bridge devices with netlink
stephen hemminger [Mon, 4 Apr 2011 14:03:32 +0000 (14:03 +0000)]
Add netlink device ops to allow creating bridge device via netlink.
This works in a manner similar to vlan, macvlan and bonding.

Example:
  # ip link add link dev br0 type bridge
  # ip link del dev br0

The change required rearranging initializtion code to deal with
being called by create link. Most of the initialization happens
in br_dev_setup, but allocation of stats is done in ndo_init callback
to deal with allocation failure. Sysfs setup has to wait until
after the network device kobject is registered.

Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

net/bridge/br.c
net/bridge/br_device.c
net/bridge/br_if.c
net/bridge/br_netlink.c
net/bridge/br_notify.c

index 84bbb82..f20c4fd 100644 (file)
@@ -104,3 +104,4 @@ module_init(br_init)
 module_exit(br_deinit)
 MODULE_LICENSE("GPL");
 MODULE_VERSION(BR_VERSION);
+MODULE_ALIAS_RTNL_LINK("bridge");
index 21e5901..45cfd54 100644 (file)
@@ -74,6 +74,17 @@ out:
        return NETDEV_TX_OK;
 }
 
+static int br_dev_init(struct net_device *dev)
+{
+       struct net_bridge *br = netdev_priv(dev);
+
+       br->stats = alloc_percpu(struct br_cpu_netstats);
+       if (!br->stats)
+               return -ENOMEM;
+
+       return 0;
+}
+
 static int br_dev_open(struct net_device *dev)
 {
        struct net_bridge *br = netdev_priv(dev);
@@ -334,6 +345,7 @@ static const struct ethtool_ops br_ethtool_ops = {
 static const struct net_device_ops br_netdev_ops = {
        .ndo_open                = br_dev_open,
        .ndo_stop                = br_dev_stop,
+       .ndo_init                = br_dev_init,
        .ndo_start_xmit          = br_dev_xmit,
        .ndo_get_stats64         = br_get_stats64,
        .ndo_set_mac_address     = br_set_mac_address,
@@ -357,18 +369,47 @@ static void br_dev_free(struct net_device *dev)
        free_netdev(dev);
 }
 
+static struct device_type br_type = {
+       .name   = "bridge",
+};
+
 void br_dev_setup(struct net_device *dev)
 {
+       struct net_bridge *br = netdev_priv(dev);
+
        random_ether_addr(dev->dev_addr);
        ether_setup(dev);
 
        dev->netdev_ops = &br_netdev_ops;
        dev->destructor = br_dev_free;
        SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
+       SET_NETDEV_DEVTYPE(dev, &br_type);
        dev->tx_queue_len = 0;
        dev->priv_flags = IFF_EBRIDGE;
 
        dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
                        NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | NETIF_F_LLTX |
                        NETIF_F_NETNS_LOCAL | NETIF_F_GSO | NETIF_F_HW_VLAN_TX;
+
+       br->dev = dev;
+       spin_lock_init(&br->lock);
+       INIT_LIST_HEAD(&br->port_list);
+       spin_lock_init(&br->hash_lock);
+
+       br->bridge_id.prio[0] = 0x80;
+       br->bridge_id.prio[1] = 0x00;
+
+       memcpy(br->group_addr, br_group_address, ETH_ALEN);
+
+       br->feature_mask = dev->features;
+       br->stp_enabled = BR_NO_STP;
+       br->designated_root = br->bridge_id;
+       br->bridge_max_age = br->max_age = 20 * HZ;
+       br->bridge_hello_time = br->hello_time = 2 * HZ;
+       br->bridge_forward_delay = br->forward_delay = 15 * HZ;
+       br->ageing_time = 300 * HZ;
+
+       br_netfilter_rtable_init(br);
+       br_stp_timer_init(br);
+       br_multicast_init(br);
 }
index 718b603..7f5379c 100644 (file)
@@ -175,56 +175,6 @@ static void del_br(struct net_bridge *br, struct list_head *head)
        unregister_netdevice_queue(br->dev, head);
 }
 
-static struct net_device *new_bridge_dev(struct net *net, const char *name)
-{
-       struct net_bridge *br;
-       struct net_device *dev;
-
-       dev = alloc_netdev(sizeof(struct net_bridge), name,
-                          br_dev_setup);
-
-       if (!dev)
-               return NULL;
-       dev_net_set(dev, net);
-
-       br = netdev_priv(dev);
-       br->dev = dev;
-
-       br->stats = alloc_percpu(struct br_cpu_netstats);
-       if (!br->stats) {
-               free_netdev(dev);
-               return NULL;
-       }
-
-       spin_lock_init(&br->lock);
-       INIT_LIST_HEAD(&br->port_list);
-       spin_lock_init(&br->hash_lock);
-
-       br->bridge_id.prio[0] = 0x80;
-       br->bridge_id.prio[1] = 0x00;
-
-       memcpy(br->group_addr, br_group_address, ETH_ALEN);
-
-       br->feature_mask = dev->features;
-       br->stp_enabled = BR_NO_STP;
-       br->designated_root = br->bridge_id;
-       br->root_path_cost = 0;
-       br->root_port = 0;
-       br->bridge_max_age = br->max_age = 20 * HZ;
-       br->bridge_hello_time = br->hello_time = 2 * HZ;
-       br->bridge_forward_delay = br->forward_delay = 15 * HZ;
-       br->topology_change = 0;
-       br->topology_change_detected = 0;
-       br->ageing_time = 300 * HZ;
-
-       br_netfilter_rtable_init(br);
-
-       br_stp_timer_init(br);
-       br_multicast_init(br);
-
-       return dev;
-}
-
 /* find an available port number */
 static int find_portno(struct net_bridge *br)
 {
@@ -277,42 +227,19 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
        return p;
 }
 
-static struct device_type br_type = {
-       .name   = "bridge",
-};
-
 int br_add_bridge(struct net *net, const char *name)
 {
        struct net_device *dev;
-       int ret;
 
-       dev = new_bridge_dev(net, name);
+       dev = alloc_netdev(sizeof(struct net_bridge), name,
+                          br_dev_setup);
+
        if (!dev)
                return -ENOMEM;
 
-       rtnl_lock();
-       if (strchr(dev->name, '%')) {
-               ret = dev_alloc_name(dev, dev->name);
-               if (ret < 0)
-                       goto out_free;
-       }
-
-       SET_NETDEV_DEVTYPE(dev, &br_type);
-
-       ret = register_netdevice(dev);
-       if (ret)
-               goto out_free;
-
-       ret = br_sysfs_addbr(dev);
-       if (ret)
-               unregister_netdevice(dev);
- out:
-       rtnl_unlock();
-       return ret;
+       dev_net_set(dev, net);
 
-out_free:
-       free_netdev(dev);
-       goto out;
+       return register_netdev(dev);
 }
 
 int br_del_bridge(struct net *net, const char *name)
index fb7d5a7..134a2ff 100644 (file)
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/etherdevice.h>
 #include <net/rtnetlink.h>
 #include <net/net_namespace.h>
 #include <net/sock.h>
+
 #include "br_private.h"
 
 static inline size_t br_nlmsg_size(void)
@@ -188,24 +190,61 @@ static int br_rtm_setlink(struct sk_buff *skb,  struct nlmsghdr *nlh, void *arg)
        return 0;
 }
 
+static int br_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+       if (tb[IFLA_ADDRESS]) {
+               if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
+                       return -EINVAL;
+               if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
+                       return -EADDRNOTAVAIL;
+       }
+
+       return 0;
+}
+
+static struct rtnl_link_ops br_link_ops __read_mostly = {
+       .kind           = "bridge",
+       .priv_size      = sizeof(struct net_bridge),
+       .setup          = br_dev_setup,
+       .validate       = br_validate,
+};
 
 int __init br_netlink_init(void)
 {
-       if (__rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo))
-               return -ENOBUFS;
-
-       /* Only the first call to __rtnl_register can fail */
-       __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL);
+       int err;
 
-       __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL);
-       __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL);
-       __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump);
+       err = rtnl_link_register(&br_link_ops);
+       if (err < 0)
+               goto err1;
+
+       err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo);
+       if (err)
+               goto err2;
+       err = __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL);
+       if (err)
+               goto err3;
+       err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL);
+       if (err)
+               goto err3;
+       err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL);
+       if (err)
+               goto err3;
+       err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump);
+       if (err)
+               goto err3;
 
        return 0;
+
+err3:
+       rtnl_unregister_all(PF_BRIDGE);
+err2:
+       rtnl_link_unregister(&br_link_ops);
+err1:
+       return err;
 }
 
 void __exit br_netlink_fini(void)
 {
+       rtnl_link_unregister(&br_link_ops);
        rtnl_unregister_all(PF_BRIDGE);
 }
-
index 7d337c9..7a03bb9 100644 (file)
@@ -36,6 +36,12 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
        struct net_bridge *br;
        int err;
 
+       /* register of bridge completed, add sysfs entries */
+       if ((dev->priv_flags && IFF_EBRIDGE) && event == NETDEV_REGISTER) {
+               br_sysfs_addbr(dev);
+               return NOTIFY_DONE;
+       }
+
        /* not a port of a bridge */
        p = br_port_get_rtnl(dev);
        if (!p)