bridge: allow creating bridge devices with netlink
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>
This commit is contained in:
parent
36fd2b63e3
commit
bb900b27a2
@ -104,3 +104,4 @@ module_init(br_init)
|
|||||||
module_exit(br_deinit)
|
module_exit(br_deinit)
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_VERSION(BR_VERSION);
|
MODULE_VERSION(BR_VERSION);
|
||||||
|
MODULE_ALIAS_RTNL_LINK("bridge");
|
||||||
|
@ -74,6 +74,17 @@ out:
|
|||||||
return NETDEV_TX_OK;
|
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)
|
static int br_dev_open(struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct net_bridge *br = netdev_priv(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 = {
|
static const struct net_device_ops br_netdev_ops = {
|
||||||
.ndo_open = br_dev_open,
|
.ndo_open = br_dev_open,
|
||||||
.ndo_stop = br_dev_stop,
|
.ndo_stop = br_dev_stop,
|
||||||
|
.ndo_init = br_dev_init,
|
||||||
.ndo_start_xmit = br_dev_xmit,
|
.ndo_start_xmit = br_dev_xmit,
|
||||||
.ndo_get_stats64 = br_get_stats64,
|
.ndo_get_stats64 = br_get_stats64,
|
||||||
.ndo_set_mac_address = br_set_mac_address,
|
.ndo_set_mac_address = br_set_mac_address,
|
||||||
@ -357,18 +369,47 @@ static void br_dev_free(struct net_device *dev)
|
|||||||
free_netdev(dev);
|
free_netdev(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct device_type br_type = {
|
||||||
|
.name = "bridge",
|
||||||
|
};
|
||||||
|
|
||||||
void br_dev_setup(struct net_device *dev)
|
void br_dev_setup(struct net_device *dev)
|
||||||
{
|
{
|
||||||
|
struct net_bridge *br = netdev_priv(dev);
|
||||||
|
|
||||||
random_ether_addr(dev->dev_addr);
|
random_ether_addr(dev->dev_addr);
|
||||||
ether_setup(dev);
|
ether_setup(dev);
|
||||||
|
|
||||||
dev->netdev_ops = &br_netdev_ops;
|
dev->netdev_ops = &br_netdev_ops;
|
||||||
dev->destructor = br_dev_free;
|
dev->destructor = br_dev_free;
|
||||||
SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
|
SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
|
||||||
|
SET_NETDEV_DEVTYPE(dev, &br_type);
|
||||||
dev->tx_queue_len = 0;
|
dev->tx_queue_len = 0;
|
||||||
dev->priv_flags = IFF_EBRIDGE;
|
dev->priv_flags = IFF_EBRIDGE;
|
||||||
|
|
||||||
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
|
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
|
||||||
NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | NETIF_F_LLTX |
|
NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | NETIF_F_LLTX |
|
||||||
NETIF_F_NETNS_LOCAL | NETIF_F_GSO | NETIF_F_HW_VLAN_TX;
|
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);
|
||||||
}
|
}
|
||||||
|
@ -175,56 +175,6 @@ static void del_br(struct net_bridge *br, struct list_head *head)
|
|||||||
unregister_netdevice_queue(br->dev, 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 */
|
/* find an available port number */
|
||||||
static int find_portno(struct net_bridge *br)
|
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;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct device_type br_type = {
|
|
||||||
.name = "bridge",
|
|
||||||
};
|
|
||||||
|
|
||||||
int br_add_bridge(struct net *net, const char *name)
|
int br_add_bridge(struct net *net, const char *name)
|
||||||
{
|
{
|
||||||
struct net_device *dev;
|
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)
|
if (!dev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
rtnl_lock();
|
dev_net_set(dev, net);
|
||||||
if (strchr(dev->name, '%')) {
|
|
||||||
ret = dev_alloc_name(dev, dev->name);
|
|
||||||
if (ret < 0)
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
SET_NETDEV_DEVTYPE(dev, &br_type);
|
return register_netdev(dev);
|
||||||
|
|
||||||
ret = register_netdevice(dev);
|
|
||||||
if (ret)
|
|
||||||
goto out_free;
|
|
||||||
|
|
||||||
ret = br_sysfs_addbr(dev);
|
|
||||||
if (ret)
|
|
||||||
unregister_netdevice(dev);
|
|
||||||
out:
|
|
||||||
rtnl_unlock();
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
out_free:
|
|
||||||
free_netdev(dev);
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int br_del_bridge(struct net *net, const char *name)
|
int br_del_bridge(struct net *net, const char *name)
|
||||||
|
@ -12,9 +12,11 @@
|
|||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/etherdevice.h>
|
||||||
#include <net/rtnetlink.h>
|
#include <net/rtnetlink.h>
|
||||||
#include <net/net_namespace.h>
|
#include <net/net_namespace.h>
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
|
|
||||||
#include "br_private.h"
|
#include "br_private.h"
|
||||||
|
|
||||||
static inline size_t br_nlmsg_size(void)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int br_validate(struct nlattr *tb[], struct nlattr *data[])
|
||||||
int __init br_netlink_init(void)
|
|
||||||
{
|
{
|
||||||
if (__rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo))
|
if (tb[IFLA_ADDRESS]) {
|
||||||
return -ENOBUFS;
|
if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
|
||||||
|
return -EINVAL;
|
||||||
/* Only the first call to __rtnl_register can fail */
|
if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
|
||||||
__rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL);
|
return -EADDRNOTAVAIL;
|
||||||
|
}
|
||||||
__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);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __exit br_netlink_fini(void)
|
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)
|
||||||
{
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
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);
|
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);
|
||||||
|
}
|
||||||
|
@ -36,6 +36,12 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
|
|||||||
struct net_bridge *br;
|
struct net_bridge *br;
|
||||||
int err;
|
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 */
|
/* not a port of a bridge */
|
||||||
p = br_port_get_rtnl(dev);
|
p = br_port_get_rtnl(dev);
|
||||||
if (!p)
|
if (!p)
|
||||||
|
Loading…
Reference in New Issue
Block a user