forked from mirrors/linux
		
	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
					
				
					 5 changed files with 104 additions and 90 deletions
				
			
		| 
						 | 
					@ -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 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 | 
				
			||||||
	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 a new issue