ipv6: fail early when creating netdev named all or default
We create a proc dir for each network device, this will cause conflicts when the devices have name "all" or "default". Rather than emitting an ugly kernel warning, we could just fail earlier by checking the device name. Reported-by: Stephane Chazelas <stephane.chazelas@gmail.com> Cc: "David S. Miller" <davem@davemloft.net> Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
20e61da7ff
commit
a317a2f19d
1 changed files with 54 additions and 28 deletions
|
@ -108,11 +108,12 @@ static inline u32 cstamp_delta(unsigned long cstamp)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static void addrconf_sysctl_register(struct inet6_dev *idev);
|
||||
static int addrconf_sysctl_register(struct inet6_dev *idev);
|
||||
static void addrconf_sysctl_unregister(struct inet6_dev *idev);
|
||||
#else
|
||||
static inline void addrconf_sysctl_register(struct inet6_dev *idev)
|
||||
static inline int addrconf_sysctl_register(struct inet6_dev *idev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void addrconf_sysctl_unregister(struct inet6_dev *idev)
|
||||
|
@ -310,16 +311,16 @@ static int snmp6_alloc_dev(struct inet6_dev *idev)
|
|||
static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
|
||||
{
|
||||
struct inet6_dev *ndev;
|
||||
int err = -ENOMEM;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (dev->mtu < IPV6_MIN_MTU)
|
||||
return NULL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL);
|
||||
|
||||
if (ndev == NULL)
|
||||
return NULL;
|
||||
return ERR_PTR(err);
|
||||
|
||||
rwlock_init(&ndev->lock);
|
||||
ndev->dev = dev;
|
||||
|
@ -332,7 +333,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
|
|||
ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
|
||||
if (ndev->nd_parms == NULL) {
|
||||
kfree(ndev);
|
||||
return NULL;
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
if (ndev->cnf.forwarding)
|
||||
dev_disable_lro(dev);
|
||||
|
@ -346,17 +347,14 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
|
|||
neigh_parms_release(&nd_tbl, ndev->nd_parms);
|
||||
dev_put(dev);
|
||||
kfree(ndev);
|
||||
return NULL;
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
if (snmp6_register_dev(ndev) < 0) {
|
||||
ADBG(KERN_WARNING
|
||||
"%s: cannot create /proc/net/dev_snmp6/%s\n",
|
||||
__func__, dev->name);
|
||||
neigh_parms_release(&nd_tbl, ndev->nd_parms);
|
||||
ndev->dead = 1;
|
||||
in6_dev_finish_destroy(ndev);
|
||||
return NULL;
|
||||
goto err_release;
|
||||
}
|
||||
|
||||
/* One reference from device. We must do this before
|
||||
|
@ -394,7 +392,12 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
|
|||
|
||||
ipv6_mc_init_dev(ndev);
|
||||
ndev->tstamp = jiffies;
|
||||
addrconf_sysctl_register(ndev);
|
||||
err = addrconf_sysctl_register(ndev);
|
||||
if (err) {
|
||||
ipv6_mc_destroy_dev(ndev);
|
||||
del_timer(&ndev->regen_timer);
|
||||
goto err_release;
|
||||
}
|
||||
/* protected by rtnl_lock */
|
||||
rcu_assign_pointer(dev->ip6_ptr, ndev);
|
||||
|
||||
|
@ -409,6 +412,12 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
|
|||
ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
|
||||
|
||||
return ndev;
|
||||
|
||||
err_release:
|
||||
neigh_parms_release(&nd_tbl, ndev->nd_parms);
|
||||
ndev->dead = 1;
|
||||
in6_dev_finish_destroy(ndev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static struct inet6_dev *ipv6_find_idev(struct net_device *dev)
|
||||
|
@ -420,7 +429,7 @@ static struct inet6_dev *ipv6_find_idev(struct net_device *dev)
|
|||
idev = __in6_dev_get(dev);
|
||||
if (!idev) {
|
||||
idev = ipv6_add_dev(dev);
|
||||
if (!idev)
|
||||
if (IS_ERR(idev))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2830,8 +2839,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
|
|||
case NETDEV_REGISTER:
|
||||
if (!idev && dev->mtu >= IPV6_MIN_MTU) {
|
||||
idev = ipv6_add_dev(dev);
|
||||
if (!idev)
|
||||
return notifier_from_errno(-ENOMEM);
|
||||
if (IS_ERR(idev))
|
||||
return notifier_from_errno(PTR_ERR(idev));
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -2851,7 +2860,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
|
|||
if (!idev && dev->mtu >= IPV6_MIN_MTU)
|
||||
idev = ipv6_add_dev(dev);
|
||||
|
||||
if (idev) {
|
||||
if (!IS_ERR_OR_NULL(idev)) {
|
||||
idev->if_flags |= IF_READY;
|
||||
run_pending = 1;
|
||||
}
|
||||
|
@ -2894,7 +2903,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
|
|||
break;
|
||||
}
|
||||
|
||||
if (idev) {
|
||||
if (!IS_ERR_OR_NULL(idev)) {
|
||||
if (run_pending)
|
||||
addrconf_dad_run(idev);
|
||||
|
||||
|
@ -2929,7 +2938,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
|
|||
|
||||
if (!idev && dev->mtu >= IPV6_MIN_MTU) {
|
||||
idev = ipv6_add_dev(dev);
|
||||
if (idev)
|
||||
if (!IS_ERR(idev))
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2950,10 +2959,14 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
|
|||
if (idev) {
|
||||
snmp6_unregister_dev(idev);
|
||||
addrconf_sysctl_unregister(idev);
|
||||
addrconf_sysctl_register(idev);
|
||||
err = snmp6_register_dev(idev);
|
||||
err = addrconf_sysctl_register(idev);
|
||||
if (err)
|
||||
return notifier_from_errno(err);
|
||||
err = snmp6_register_dev(idev);
|
||||
if (err) {
|
||||
addrconf_sysctl_unregister(idev);
|
||||
return notifier_from_errno(err);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -5248,12 +5261,23 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p)
|
|||
kfree(t);
|
||||
}
|
||||
|
||||
static void addrconf_sysctl_register(struct inet6_dev *idev)
|
||||
static int addrconf_sysctl_register(struct inet6_dev *idev)
|
||||
{
|
||||
neigh_sysctl_register(idev->dev, idev->nd_parms,
|
||||
int err;
|
||||
|
||||
if (!sysctl_dev_name_is_allowed(idev->dev->name))
|
||||
return -EINVAL;
|
||||
|
||||
err = neigh_sysctl_register(idev->dev, idev->nd_parms,
|
||||
&ndisc_ifinfo_sysctl_change);
|
||||
__addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,
|
||||
if (err)
|
||||
return err;
|
||||
err = __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,
|
||||
idev, &idev->cnf);
|
||||
if (err)
|
||||
neigh_sysctl_unregister(idev->nd_parms);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void addrconf_sysctl_unregister(struct inet6_dev *idev)
|
||||
|
@ -5338,6 +5362,7 @@ static struct rtnl_af_ops inet6_ops = {
|
|||
|
||||
int __init addrconf_init(void)
|
||||
{
|
||||
struct inet6_dev *idev;
|
||||
int i, err;
|
||||
|
||||
err = ipv6_addr_label_init();
|
||||
|
@ -5376,11 +5401,12 @@ int __init addrconf_init(void)
|
|||
* device and it being up should be removed.
|
||||
*/
|
||||
rtnl_lock();
|
||||
if (!ipv6_add_dev(init_net.loopback_dev))
|
||||
err = -ENOMEM;
|
||||
idev = ipv6_add_dev(init_net.loopback_dev);
|
||||
rtnl_unlock();
|
||||
if (err)
|
||||
if (IS_ERR(idev)) {
|
||||
err = PTR_ERR(idev);
|
||||
goto errlo;
|
||||
}
|
||||
|
||||
for (i = 0; i < IN6_ADDR_HSIZE; i++)
|
||||
INIT_HLIST_HEAD(&inet6_addr_lst[i]);
|
||||
|
|
Loading…
Reference in a new issue