Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb900b27 authored by stephen hemminger's avatar stephen hemminger Committed by David S. Miller
Browse files

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: default avatarStephen Hemminger <shemminger@vyatta.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 36fd2b63
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -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");
+41 −0
Original line number Original line Diff line number Diff line
@@ -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);
}
}
+5 −78
Original line number Original line Diff line number Diff line
@@ -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);

	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:
	return register_netdev(dev);
	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)
+48 −9
Original line number Original line Diff line number Diff line
@@ -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[])
{
	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)
int __init br_netlink_init(void)
{
{
	if (__rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo))
	int err;
		return -ENOBUFS;

	/* Only the first call to __rtnl_register can fail */
	__rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL);


	__rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL);
	err = rtnl_link_register(&br_link_ops);
	__rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL);
	if (err < 0)
	__rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump);
		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;
	return 0;

err3:
	rtnl_unregister_all(PF_BRIDGE);
err2:
	rtnl_link_unregister(&br_link_ops);
err1:
	return err;
}
}


void __exit br_netlink_fini(void)
void __exit br_netlink_fini(void)
{
{
	rtnl_link_unregister(&br_link_ops);
	rtnl_unregister_all(PF_BRIDGE);
	rtnl_unregister_all(PF_BRIDGE);
}
}
+6 −0
Original line number Original line Diff line number Diff line
@@ -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)