rtnetlink: support specifying device flags on device creation
commit e8469ed959c373c2ff9e6f488aa5a14971aebe1f Author: Patrick McHardy <kaber@trash.net> Date: Tue Feb 23 20:41:30 2010 +0100 Support specifying the initial device flags when creating a device though rtnl_link. Devices allocated by rtnl_create_link() are marked as INITIALIZING in order to surpress netlink registration notifications. To complete setup, rtnl_configure_link() must be called, which performs the device flag changes and invokes the deferred notifiers if everything went well. Two examples: # add macvlan to eth0 # $ ip link add link eth0 up allmulticast on type macvlan [LINK]11: macvlan0@eth0: <BROADCAST,MULTICAST,ALLMULTI,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN link/ether 26:f8:84:02:f9:2a brd ff:ff:ff:ff:ff:ff [ROUTE]ff00::/8 dev macvlan0 table local metric 256 mtu 1500 advmss 1440 hoplimit 0 [ROUTE]fe80::/64 dev macvlan0 proto kernel metric 256 mtu 1500 advmss 1440 hoplimit 0 [LINK]11: macvlan0@eth0: <BROADCAST,MULTICAST,ALLMULTI,UP,LOWER_UP> mtu 1500 link/ether 26:f8:84:02:f9:2a [ADDR]11: macvlan0 inet6 fe80::24f8:84ff:fe02:f92a/64 scope link valid_lft forever preferred_lft forever [ROUTE]local fe80::24f8:84ff:fe02:f92a via :: dev lo table local proto none metric 0 mtu 16436 advmss 16376 hoplimit 0 [ROUTE]default via fe80::215:e9ff:fef0:10f8 dev macvlan0 proto kernel metric 1024 mtu 1500 advmss 1440 hoplimit 0 [NEIGH]fe80::215:e9ff:fef0:10f8 dev macvlan0 lladdr 00:15:e9:f0:10:f8 router STALE [ROUTE]2001:6f8:974::/64 dev macvlan0 proto kernel metric 256 expires 0sec mtu 1500 advmss 1440 hoplimit 0 [PREFIX]prefix 2001:6f8:974::/64 dev macvlan0 onlink autoconf valid 14400 preferred 131084 [ADDR]11: macvlan0 inet6 2001:6f8:974:0:24f8:84ff:fe02:f92a/64 scope global dynamic valid_lft 86399sec preferred_lft 14399sec # add VLAN to eth1, eth1 is down # $ ip link add link eth1 up type vlan id 1000 RTNETLINK answers: Network is down <no events> Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
bd38081160
commit
3729d50212
|
@ -333,19 +333,17 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
|
||||||
struct veth_priv *priv;
|
struct veth_priv *priv;
|
||||||
char ifname[IFNAMSIZ];
|
char ifname[IFNAMSIZ];
|
||||||
struct nlattr *peer_tb[IFLA_MAX + 1], **tbp;
|
struct nlattr *peer_tb[IFLA_MAX + 1], **tbp;
|
||||||
|
struct ifinfomsg *ifmp;
|
||||||
struct net *net;
|
struct net *net;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* create and register peer first
|
* create and register peer first
|
||||||
*
|
|
||||||
* struct ifinfomsg is at the head of VETH_INFO_PEER, but we
|
|
||||||
* skip it since no info from it is useful yet
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (data != NULL && data[VETH_INFO_PEER] != NULL) {
|
if (data != NULL && data[VETH_INFO_PEER] != NULL) {
|
||||||
struct nlattr *nla_peer;
|
struct nlattr *nla_peer;
|
||||||
|
|
||||||
nla_peer = data[VETH_INFO_PEER];
|
nla_peer = data[VETH_INFO_PEER];
|
||||||
|
ifmp = nla_data(nla_peer);
|
||||||
err = nla_parse(peer_tb, IFLA_MAX,
|
err = nla_parse(peer_tb, IFLA_MAX,
|
||||||
nla_data(nla_peer) + sizeof(struct ifinfomsg),
|
nla_data(nla_peer) + sizeof(struct ifinfomsg),
|
||||||
nla_len(nla_peer) - sizeof(struct ifinfomsg),
|
nla_len(nla_peer) - sizeof(struct ifinfomsg),
|
||||||
|
@ -358,8 +356,10 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
tbp = peer_tb;
|
tbp = peer_tb;
|
||||||
} else
|
} else {
|
||||||
|
ifmp = NULL;
|
||||||
tbp = tb;
|
tbp = tb;
|
||||||
|
}
|
||||||
|
|
||||||
if (tbp[IFLA_IFNAME])
|
if (tbp[IFLA_IFNAME])
|
||||||
nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ);
|
nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ);
|
||||||
|
@ -387,6 +387,10 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
|
||||||
|
|
||||||
netif_carrier_off(peer);
|
netif_carrier_off(peer);
|
||||||
|
|
||||||
|
err = rtnl_configure_link(peer, ifmp);
|
||||||
|
if (err < 0)
|
||||||
|
goto err_configure_peer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* register dev last
|
* register dev last
|
||||||
*
|
*
|
||||||
|
@ -428,6 +432,7 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
|
||||||
err_register_dev:
|
err_register_dev:
|
||||||
/* nothing to do */
|
/* nothing to do */
|
||||||
err_alloc_name:
|
err_alloc_name:
|
||||||
|
err_configure_peer:
|
||||||
unregister_netdevice(peer);
|
unregister_netdevice(peer);
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,8 @@ extern void rtnl_link_unregister(struct rtnl_link_ops *ops);
|
||||||
extern struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]);
|
extern struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]);
|
||||||
extern struct net_device *rtnl_create_link(struct net *src_net, struct net *net,
|
extern struct net_device *rtnl_create_link(struct net *src_net, struct net *net,
|
||||||
char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]);
|
char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]);
|
||||||
|
extern int rtnl_configure_link(struct net_device *dev,
|
||||||
|
const struct ifinfomsg *ifm);
|
||||||
extern const struct nla_policy ifla_policy[IFLA_MAX+1];
|
extern const struct nla_policy ifla_policy[IFLA_MAX+1];
|
||||||
|
|
||||||
#define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind)
|
#define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind)
|
||||||
|
|
|
@ -549,6 +549,19 @@ static void set_operstate(struct net_device *dev, unsigned char transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int rtnl_dev_combine_flags(const struct net_device *dev,
|
||||||
|
const struct ifinfomsg *ifm)
|
||||||
|
{
|
||||||
|
unsigned int flags = ifm->ifi_flags;
|
||||||
|
|
||||||
|
/* bugwards compatibility: ifi_change == 0 is treated as ~0 */
|
||||||
|
if (ifm->ifi_change)
|
||||||
|
flags = (flags & ifm->ifi_change) |
|
||||||
|
(dev->flags & ~ifm->ifi_change);
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
|
static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
|
||||||
const struct net_device_stats *b)
|
const struct net_device_stats *b)
|
||||||
{
|
{
|
||||||
|
@ -904,13 +917,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ifm->ifi_flags || ifm->ifi_change) {
|
if (ifm->ifi_flags || ifm->ifi_change) {
|
||||||
unsigned int flags = ifm->ifi_flags;
|
err = dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm));
|
||||||
|
|
||||||
/* bugwards compatibility: ifi_change == 0 is treated as ~0 */
|
|
||||||
if (ifm->ifi_change)
|
|
||||||
flags = (flags & ifm->ifi_change) |
|
|
||||||
(dev->flags & ~ifm->ifi_change);
|
|
||||||
err = dev_change_flags(dev, flags);
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto errout;
|
goto errout;
|
||||||
}
|
}
|
||||||
|
@ -1053,6 +1060,26 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)
|
||||||
|
{
|
||||||
|
unsigned int old_flags;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
old_flags = dev->flags;
|
||||||
|
if (ifm && (ifm->ifi_flags || ifm->ifi_change)) {
|
||||||
|
err = __dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->rtnl_link_state = RTNL_LINK_INITIALIZED;
|
||||||
|
rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
|
||||||
|
|
||||||
|
__dev_notify_flags(dev, old_flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(rtnl_configure_link);
|
||||||
|
|
||||||
struct net_device *rtnl_create_link(struct net *src_net, struct net *net,
|
struct net_device *rtnl_create_link(struct net *src_net, struct net *net,
|
||||||
char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[])
|
char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[])
|
||||||
{
|
{
|
||||||
|
@ -1074,6 +1101,7 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net,
|
||||||
|
|
||||||
dev_net_set(dev, net);
|
dev_net_set(dev, net);
|
||||||
dev->rtnl_link_ops = ops;
|
dev->rtnl_link_ops = ops;
|
||||||
|
dev->rtnl_link_state = RTNL_LINK_INITIALIZING;
|
||||||
dev->real_num_tx_queues = real_num_queues;
|
dev->real_num_tx_queues = real_num_queues;
|
||||||
|
|
||||||
if (strchr(dev->name, '%')) {
|
if (strchr(dev->name, '%')) {
|
||||||
|
@ -1203,7 +1231,7 @@ replay:
|
||||||
if (!(nlh->nlmsg_flags & NLM_F_CREATE))
|
if (!(nlh->nlmsg_flags & NLM_F_CREATE))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (ifm->ifi_index || ifm->ifi_flags || ifm->ifi_change)
|
if (ifm->ifi_index)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
if (tb[IFLA_MAP] || tb[IFLA_MASTER] || tb[IFLA_PROTINFO])
|
if (tb[IFLA_MAP] || tb[IFLA_MASTER] || tb[IFLA_PROTINFO])
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
@ -1234,9 +1262,15 @@ replay:
|
||||||
err = ops->newlink(net, dev, tb, data);
|
err = ops->newlink(net, dev, tb, data);
|
||||||
else
|
else
|
||||||
err = register_netdevice(dev);
|
err = register_netdevice(dev);
|
||||||
if (err < 0 && !IS_ERR(dev))
|
if (err < 0 && !IS_ERR(dev)) {
|
||||||
free_netdev(dev);
|
free_netdev(dev);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rtnl_configure_link(dev, ifm);
|
||||||
|
if (err < 0)
|
||||||
|
unregister_netdevice(dev);
|
||||||
|
out:
|
||||||
put_net(dest_net);
|
put_net(dest_net);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue