diff options
Diffstat (limited to 'drivers/net/ipvlan/ipvlan_main.c')
| -rw-r--r-- | drivers/net/ipvlan/ipvlan_main.c | 78 | 
1 files changed, 75 insertions, 3 deletions
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 7c7680c8f0e3..f37e3c1fd4e7 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -455,7 +455,8 @@ static const struct ethtool_ops ipvlan_ethtool_ops = {  };  static int ipvlan_nl_changelink(struct net_device *dev, -				struct nlattr *tb[], struct nlattr *data[]) +				struct nlattr *tb[], struct nlattr *data[], +				struct netlink_ext_ack *extack)  {  	struct ipvl_dev *ipvlan = netdev_priv(dev);  	struct ipvl_port *port = ipvlan_port_get_rtnl(ipvlan->phy_dev); @@ -476,7 +477,8 @@ static size_t ipvlan_nl_getsize(const struct net_device *dev)  		);  } -static int ipvlan_nl_validate(struct nlattr *tb[], struct nlattr *data[]) +static int ipvlan_nl_validate(struct nlattr *tb[], struct nlattr *data[], +			      struct netlink_ext_ack *extack)  {  	if (data && data[IFLA_IPVLAN_MODE]) {  		u16 mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); @@ -508,7 +510,8 @@ err:  }  int ipvlan_link_new(struct net *src_net, struct net_device *dev, -		    struct nlattr *tb[], struct nlattr *data[]) +		    struct nlattr *tb[], struct nlattr *data[], +		    struct netlink_ext_ack *extack)  {  	struct ipvl_dev *ipvlan = netdev_priv(dev);  	struct ipvl_port *port; @@ -824,6 +827,33 @@ static int ipvlan_addr6_event(struct notifier_block *unused,  	return NOTIFY_OK;  } +static int ipvlan_addr6_validator_event(struct notifier_block *unused, +					unsigned long event, void *ptr) +{ +	struct in6_validator_info *i6vi = (struct in6_validator_info *)ptr; +	struct net_device *dev = (struct net_device *)i6vi->i6vi_dev->dev; +	struct ipvl_dev *ipvlan = netdev_priv(dev); + +	/* FIXME IPv6 autoconf calls us from bh without RTNL */ +	if (in_softirq()) +		return NOTIFY_DONE; + +	if (!netif_is_ipvlan(dev)) +		return NOTIFY_DONE; + +	if (!ipvlan || !ipvlan->port) +		return NOTIFY_DONE; + +	switch (event) { +	case NETDEV_UP: +		if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true)) +			return notifier_from_errno(-EADDRINUSE); +		break; +	} + +	return NOTIFY_OK; +} +  static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)  {  	if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) { @@ -871,10 +901,37 @@ static int ipvlan_addr4_event(struct notifier_block *unused,  	return NOTIFY_OK;  } +static int ipvlan_addr4_validator_event(struct notifier_block *unused, +					unsigned long event, void *ptr) +{ +	struct in_validator_info *ivi = (struct in_validator_info *)ptr; +	struct net_device *dev = (struct net_device *)ivi->ivi_dev->dev; +	struct ipvl_dev *ipvlan = netdev_priv(dev); + +	if (!netif_is_ipvlan(dev)) +		return NOTIFY_DONE; + +	if (!ipvlan || !ipvlan->port) +		return NOTIFY_DONE; + +	switch (event) { +	case NETDEV_UP: +		if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false)) +			return notifier_from_errno(-EADDRINUSE); +		break; +	} + +	return NOTIFY_OK; +} +  static struct notifier_block ipvlan_addr4_notifier_block __read_mostly = {  	.notifier_call = ipvlan_addr4_event,  }; +static struct notifier_block ipvlan_addr4_vtor_notifier_block __read_mostly = { +	.notifier_call = ipvlan_addr4_validator_event, +}; +  static struct notifier_block ipvlan_notifier_block __read_mostly = {  	.notifier_call = ipvlan_device_event,  }; @@ -883,6 +940,10 @@ static struct notifier_block ipvlan_addr6_notifier_block __read_mostly = {  	.notifier_call = ipvlan_addr6_event,  }; +static struct notifier_block ipvlan_addr6_vtor_notifier_block __read_mostly = { +	.notifier_call = ipvlan_addr6_validator_event, +}; +  static void ipvlan_ns_exit(struct net *net)  {  	struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid); @@ -907,7 +968,10 @@ static int __init ipvlan_init_module(void)  	ipvlan_init_secret();  	register_netdevice_notifier(&ipvlan_notifier_block);  	register_inet6addr_notifier(&ipvlan_addr6_notifier_block); +	register_inet6addr_validator_notifier( +	    &ipvlan_addr6_vtor_notifier_block);  	register_inetaddr_notifier(&ipvlan_addr4_notifier_block); +	register_inetaddr_validator_notifier(&ipvlan_addr4_vtor_notifier_block);  	err = register_pernet_subsys(&ipvlan_net_ops);  	if (err < 0) @@ -922,7 +986,11 @@ static int __init ipvlan_init_module(void)  	return 0;  error:  	unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block); +	unregister_inetaddr_validator_notifier( +	    &ipvlan_addr4_vtor_notifier_block);  	unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block); +	unregister_inet6addr_validator_notifier( +	    &ipvlan_addr6_vtor_notifier_block);  	unregister_netdevice_notifier(&ipvlan_notifier_block);  	return err;  } @@ -933,7 +1001,11 @@ static void __exit ipvlan_cleanup_module(void)  	unregister_pernet_subsys(&ipvlan_net_ops);  	unregister_netdevice_notifier(&ipvlan_notifier_block);  	unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block); +	unregister_inetaddr_validator_notifier( +	    &ipvlan_addr4_vtor_notifier_block);  	unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block); +	unregister_inet6addr_validator_notifier( +	    &ipvlan_addr6_vtor_notifier_block);  }  module_init(ipvlan_init_module);  | 
