diff options
Diffstat (limited to 'net/core/rtnetlink.c')
| -rw-r--r-- | net/core/rtnetlink.c | 110 | 
1 files changed, 83 insertions, 27 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 1063996f8317..f0493e3b7471 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -299,7 +299,12 @@ int __rtnl_link_register(struct rtnl_link_ops *ops)  	if (rtnl_link_ops_get(ops->kind))  		return -EEXIST; -	if (!ops->dellink) +	/* The check for setup is here because if ops +	 * does not have that filled up, it is not possible +	 * to use the ops for creating device. So do not +	 * fill up dellink as well. That disables rtnl_dellink. +	 */ +	if (ops->setup && !ops->dellink)  		ops->dellink = unregister_netdevice_queue;  	list_add_tail(&ops->list, &link_ops); @@ -799,7 +804,8 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev,  			(nla_total_size(sizeof(struct ifla_vf_mac)) +  			 nla_total_size(sizeof(struct ifla_vf_vlan)) +  			 nla_total_size(sizeof(struct ifla_vf_spoofchk)) + -			 nla_total_size(sizeof(struct ifla_vf_rate))); +			 nla_total_size(sizeof(struct ifla_vf_rate)) + +			 nla_total_size(sizeof(struct ifla_vf_link_state)));  		return size;  	} else  		return 0; @@ -1777,7 +1783,7 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)  		return -ENODEV;  	ops = dev->rtnl_link_ops; -	if (!ops) +	if (!ops || !ops->dellink)  		return -EOPNOTSUPP;  	ops->dellink(dev, &list_kill); @@ -1805,7 +1811,8 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)  EXPORT_SYMBOL(rtnl_configure_link);  struct net_device *rtnl_create_link(struct net *net, -	char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]) +	char *ifname, unsigned char name_assign_type, +	const struct rtnl_link_ops *ops, struct nlattr *tb[])  {  	int err;  	struct net_device *dev; @@ -1823,8 +1830,8 @@ struct net_device *rtnl_create_link(struct net *net,  		num_rx_queues = ops->get_num_rx_queues();  	err = -ENOMEM; -	dev = alloc_netdev_mqs(ops->priv_size, ifname, ops->setup, -			       num_tx_queues, num_rx_queues); +	dev = alloc_netdev_mqs(ops->priv_size, ifname, name_assign_type, +			       ops->setup, num_tx_queues, num_rx_queues);  	if (!dev)  		goto err; @@ -1889,6 +1896,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh)  	char ifname[IFNAMSIZ];  	struct nlattr *tb[IFLA_MAX+1];  	struct nlattr *linkinfo[IFLA_INFO_MAX+1]; +	unsigned char name_assign_type = NET_NAME_USER;  	int err;  #ifdef CONFIG_MODULES @@ -2038,14 +2046,19 @@ replay:  			return -EOPNOTSUPP;  		} -		if (!ifname[0]) +		if (!ops->setup) +			return -EOPNOTSUPP; + +		if (!ifname[0]) {  			snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind); +			name_assign_type = NET_NAME_ENUM; +		}  		dest_net = rtnl_link_get_net(net, tb);  		if (IS_ERR(dest_net))  			return PTR_ERR(dest_net); -		dev = rtnl_create_link(dest_net, ifname, ops, tb); +		dev = rtnl_create_link(dest_net, ifname, name_assign_type, ops, tb);  		if (IS_ERR(dev)) {  			err = PTR_ERR(dev);  			goto out; @@ -2380,22 +2393,20 @@ int ndo_dflt_fdb_del(struct ndmsg *ndm,  		     struct net_device *dev,  		     const unsigned char *addr)  { -	int err = -EOPNOTSUPP; +	int err = -EINVAL;  	/* If aging addresses are supported device will need to  	 * implement its own handler for this.  	 */  	if (!(ndm->ndm_state & NUD_PERMANENT)) {  		pr_info("%s: FDB only supports static addresses\n", dev->name); -		return -EINVAL; +		return err;  	}  	if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))  		err = dev_uc_del(dev, addr);  	else if (is_multicast_ether_addr(addr))  		err = dev_mc_del(dev, addr); -	else -		err = -EINVAL;  	return err;  } @@ -2509,6 +2520,7 @@ skip:  int ndo_dflt_fdb_dump(struct sk_buff *skb,  		      struct netlink_callback *cb,  		      struct net_device *dev, +		      struct net_device *filter_dev,  		      int idx)  {  	int err; @@ -2526,28 +2538,72 @@ EXPORT_SYMBOL(ndo_dflt_fdb_dump);  static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)  { -	int idx = 0; -	struct net *net = sock_net(skb->sk);  	struct net_device *dev; +	struct nlattr *tb[IFLA_MAX+1]; +	struct net_device *bdev = NULL; +	struct net_device *br_dev = NULL; +	const struct net_device_ops *ops = NULL; +	const struct net_device_ops *cops = NULL; +	struct ifinfomsg *ifm = nlmsg_data(cb->nlh); +	struct net *net = sock_net(skb->sk); +	int brport_idx = 0; +	int br_idx = 0; +	int idx = 0; -	rcu_read_lock(); -	for_each_netdev_rcu(net, dev) { -		if (dev->priv_flags & IFF_BRIDGE_PORT) { -			struct net_device *br_dev; -			const struct net_device_ops *ops; +	if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX, +			ifla_policy) == 0) { +		if (tb[IFLA_MASTER]) +			br_idx = nla_get_u32(tb[IFLA_MASTER]); +	} + +	brport_idx = ifm->ifi_index; -			br_dev = netdev_master_upper_dev_get(dev); -			ops = br_dev->netdev_ops; -			if (ops->ndo_fdb_dump) -				idx = ops->ndo_fdb_dump(skb, cb, dev, idx); +	if (br_idx) { +		br_dev = __dev_get_by_index(net, br_idx); +		if (!br_dev) +			return -ENODEV; + +		ops = br_dev->netdev_ops; +		bdev = br_dev; +	} + +	for_each_netdev(net, dev) { +		if (brport_idx && (dev->ifindex != brport_idx)) +			continue; + +		if (!br_idx) { /* user did not specify a specific bridge */ +			if (dev->priv_flags & IFF_BRIDGE_PORT) { +				br_dev = netdev_master_upper_dev_get(dev); +				cops = br_dev->netdev_ops; +			} + +			bdev = dev; +		} else { +			if (dev != br_dev && +			    !(dev->priv_flags & IFF_BRIDGE_PORT)) +				continue; + +			if (br_dev != netdev_master_upper_dev_get(dev) && +			    !(dev->priv_flags & IFF_EBRIDGE)) +				continue; + +			bdev = br_dev; +			cops = ops; +		} + +		if (dev->priv_flags & IFF_BRIDGE_PORT) { +			if (cops && cops->ndo_fdb_dump) +				idx = cops->ndo_fdb_dump(skb, cb, br_dev, dev, +							 idx);  		} +		idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx);  		if (dev->netdev_ops->ndo_fdb_dump) -			idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx); -		else -			idx = ndo_dflt_fdb_dump(skb, cb, dev, idx); +			idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, bdev, dev, +							    idx); + +		cops = NULL;  	} -	rcu_read_unlock();  	cb->args[0] = idx;  	return skb->len;  |