diff options
Diffstat (limited to 'drivers/net/macvlan.c')
| -rw-r--r-- | drivers/net/macvlan.c | 39 | 
1 files changed, 34 insertions, 5 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 6e91931a1c2c..16b43bf544b7 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -337,8 +337,11 @@ static int macvlan_open(struct net_device *dev)  	int err;  	if (vlan->port->passthru) { -		if (!(vlan->flags & MACVLAN_FLAG_NOPROMISC)) -			dev_set_promiscuity(lowerdev, 1); +		if (!(vlan->flags & MACVLAN_FLAG_NOPROMISC)) { +			err = dev_set_promiscuity(lowerdev, 1); +			if (err < 0) +				goto out; +		}  		goto hash_add;  	} @@ -638,6 +641,14 @@ static int macvlan_ethtool_get_settings(struct net_device *dev,  	return __ethtool_get_settings(vlan->lowerdev, cmd);  } +static netdev_features_t macvlan_fix_features(struct net_device *dev, +					      netdev_features_t features) +{ +	struct macvlan_dev *vlan = netdev_priv(dev); + +	return features & (vlan->set_features | ~MACVLAN_FEATURES); +} +  static const struct ethtool_ops macvlan_ethtool_ops = {  	.get_link		= ethtool_op_get_link,  	.get_settings		= macvlan_ethtool_get_settings, @@ -651,6 +662,7 @@ static const struct net_device_ops macvlan_netdev_ops = {  	.ndo_stop		= macvlan_stop,  	.ndo_start_xmit		= macvlan_start_xmit,  	.ndo_change_mtu		= macvlan_change_mtu, +	.ndo_fix_features	= macvlan_fix_features,  	.ndo_change_rx_flags	= macvlan_change_rx_flags,  	.ndo_set_mac_address	= macvlan_set_mac_address,  	.ndo_set_rx_mode	= macvlan_set_mac_lists, @@ -727,6 +739,10 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[])  			return -EADDRNOTAVAIL;  	} +	if (data && data[IFLA_MACVLAN_FLAGS] && +	    nla_get_u16(data[IFLA_MACVLAN_FLAGS]) & ~MACVLAN_FLAG_NOPROMISC) +		return -EINVAL; +  	if (data && data[IFLA_MACVLAN_MODE]) {  		switch (nla_get_u32(data[IFLA_MACVLAN_MODE])) {  		case MACVLAN_MODE_PRIVATE: @@ -791,6 +807,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,  	vlan->port     = port;  	vlan->receive  = receive;  	vlan->forward  = forward; +	vlan->set_features = MACVLAN_FEATURES;  	vlan->mode     = MACVLAN_MODE_VEPA;  	if (data && data[IFLA_MACVLAN_MODE]) @@ -853,6 +870,18 @@ static int macvlan_changelink(struct net_device *dev,  		struct nlattr *tb[], struct nlattr *data[])  {  	struct macvlan_dev *vlan = netdev_priv(dev); +	enum macvlan_mode mode; +	bool set_mode = false; + +	/* Validate mode, but don't set yet: setting flags may fail. */ +	if (data && data[IFLA_MACVLAN_MODE]) { +		set_mode = true; +		mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); +		/* Passthrough mode can't be set or cleared dynamically */ +		if ((mode == MACVLAN_MODE_PASSTHRU) != +		    (vlan->mode == MACVLAN_MODE_PASSTHRU)) +			return -EINVAL; +	}  	if (data && data[IFLA_MACVLAN_FLAGS]) {  		__u16 flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]); @@ -869,8 +898,8 @@ static int macvlan_changelink(struct net_device *dev,  		}  		vlan->flags = flags;  	} -	if (data && data[IFLA_MACVLAN_MODE]) -		vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); +	if (set_mode) +		vlan->mode = mode;  	return 0;  } @@ -927,7 +956,7 @@ static struct rtnl_link_ops macvlan_link_ops = {  static int macvlan_device_event(struct notifier_block *unused,  				unsigned long event, void *ptr)  { -	struct net_device *dev = ptr; +	struct net_device *dev = netdev_notifier_info_to_dev(ptr);  	struct macvlan_dev *vlan, *next;  	struct macvlan_port *port;  	LIST_HEAD(list_kill);  | 
