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 @@ 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 stephen hemminger
				stephen hemminger