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:
stephen hemminger
2011-04-04 14:03:32 +00:00
committed by David S. Miller
parent 36fd2b63e3
commit bb900b27a2
5 changed files with 104 additions and 90 deletions

View File

@ -12,9 +12,11 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/etherdevice.h>
#include <net/rtnetlink.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include "br_private.h"
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;
}
int __init br_netlink_init(void)
static int br_validate(struct nlattr *tb[], struct nlattr *data[])
{
if (__rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo))
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);
__rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL);
__rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump);
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;
}
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);
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);
}