diff options
Diffstat (limited to 'net/core/rtnetlink.c')
| -rw-r--r-- | net/core/rtnetlink.c | 86 | 
1 files changed, 81 insertions, 5 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 2d65c6bb24c1..49f7ea5b4c75 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -868,6 +868,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,  		   netif_running(dev) ? dev->operstate : IF_OPER_DOWN);  	NLA_PUT_U8(skb, IFLA_LINKMODE, dev->link_mode);  	NLA_PUT_U32(skb, IFLA_MTU, dev->mtu); +	NLA_PUT_U32(skb, IFLA_GROUP, dev->group);  	if (dev->ifindex != dev->iflink)  		NLA_PUT_U32(skb, IFLA_LINK, dev->iflink); @@ -1035,6 +1036,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {  	[IFLA_MAP]		= { .len = sizeof(struct rtnl_link_ifmap) },  	[IFLA_MTU]		= { .type = NLA_U32 },  	[IFLA_LINK]		= { .type = NLA_U32 }, +	[IFLA_MASTER]		= { .type = NLA_U32 },  	[IFLA_TXQLEN]		= { .type = NLA_U32 },  	[IFLA_WEIGHT]		= { .type = NLA_U32 },  	[IFLA_OPERSTATE]	= { .type = NLA_U8 }, @@ -1177,6 +1179,41 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr)  	return err;  } +static int do_set_master(struct net_device *dev, int ifindex) +{ +	struct net_device *master_dev; +	const struct net_device_ops *ops; +	int err; + +	if (dev->master) { +		if (dev->master->ifindex == ifindex) +			return 0; +		ops = dev->master->netdev_ops; +		if (ops->ndo_del_slave) { +			err = ops->ndo_del_slave(dev->master, dev); +			if (err) +				return err; +		} else { +			return -EOPNOTSUPP; +		} +	} + +	if (ifindex) { +		master_dev = __dev_get_by_index(dev_net(dev), ifindex); +		if (!master_dev) +			return -EINVAL; +		ops = master_dev->netdev_ops; +		if (ops->ndo_add_slave) { +			err = ops->ndo_add_slave(master_dev, dev); +			if (err) +				return err; +		} else { +			return -EOPNOTSUPP; +		} +	} +	return 0; +} +  static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,  		      struct nlattr **tb, char *ifname, int modified)  { @@ -1264,6 +1301,11 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,  		modified = 1;  	} +	if (tb[IFLA_GROUP]) { +		dev_set_group(dev, nla_get_u32(tb[IFLA_GROUP])); +		modified = 1; +	} +  	/*  	 * Interface selected by interface index but interface  	 * name provided implies that a name change has been @@ -1295,6 +1337,13 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,  			goto errout;  	} +	if (tb[IFLA_MASTER]) { +		err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER])); +		if (err) +			goto errout; +		modified = 1; +	} +  	if (tb[IFLA_TXQLEN])  		dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]); @@ -1541,6 +1590,8 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net,  		set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));  	if (tb[IFLA_LINKMODE])  		dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]); +	if (tb[IFLA_GROUP]) +		dev_set_group(dev, nla_get_u32(tb[IFLA_GROUP]));  	return dev; @@ -1551,6 +1602,24 @@ err:  }  EXPORT_SYMBOL(rtnl_create_link); +static int rtnl_group_changelink(struct net *net, int group, +		struct ifinfomsg *ifm, +		struct nlattr **tb) +{ +	struct net_device *dev; +	int err; + +	for_each_netdev(net, dev) { +		if (dev->group == group) { +			err = do_setlink(dev, ifm, tb, NULL, 0); +			if (err < 0) +				return err; +		} +	} + +	return 0; +} +  static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)  {  	struct net *net = sock_net(skb->sk); @@ -1578,10 +1647,12 @@ replay:  	ifm = nlmsg_data(nlh);  	if (ifm->ifi_index > 0)  		dev = __dev_get_by_index(net, ifm->ifi_index); -	else if (ifname[0]) -		dev = __dev_get_by_name(net, ifname); -	else -		dev = NULL; +	else { +		if (ifname[0]) +			dev = __dev_get_by_name(net, ifname); +		else +			dev = NULL; +	}  	err = validate_linkmsg(dev, tb);  	if (err < 0) @@ -1645,8 +1716,13 @@ replay:  			return do_setlink(dev, ifm, tb, ifname, modified);  		} -		if (!(nlh->nlmsg_flags & NLM_F_CREATE)) +		if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { +			if (ifm->ifi_index == 0 && tb[IFLA_GROUP]) +				return rtnl_group_changelink(net, +						nla_get_u32(tb[IFLA_GROUP]), +						ifm, tb);  			return -ENODEV; +		}  		if (ifm->ifi_index)  			return -EOPNOTSUPP;  | 
