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

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
...@@ -104,3 +104,4 @@ module_init(br_init) ...@@ -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) ...@@ -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 = { ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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)
......
...@@ -12,9 +12,11 @@ ...@@ -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) ...@@ -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);
} }
...@@ -36,6 +36,12 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v ...@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment