Commit a317a2f1 authored by WANG Cong's avatar WANG Cong Committed by David S. Miller

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: default avatarStephane Chazelas <stephane.chazelas@gmail.com>
Cc: "David S. Miller" <davem@davemloft.net>
Signed-off-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 20e61da7
...@@ -108,11 +108,12 @@ static inline u32 cstamp_delta(unsigned long cstamp) ...@@ -108,11 +108,12 @@ static inline u32 cstamp_delta(unsigned long cstamp)
} }
#ifdef CONFIG_SYSCTL #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); static void addrconf_sysctl_unregister(struct inet6_dev *idev);
#else #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) static inline void addrconf_sysctl_unregister(struct inet6_dev *idev)
...@@ -310,16 +311,16 @@ static int snmp6_alloc_dev(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) static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
{ {
struct inet6_dev *ndev; struct inet6_dev *ndev;
int err = -ENOMEM;
ASSERT_RTNL(); ASSERT_RTNL();
if (dev->mtu < IPV6_MIN_MTU) if (dev->mtu < IPV6_MIN_MTU)
return NULL; return ERR_PTR(-EINVAL);
ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL); ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL);
if (ndev == NULL) if (ndev == NULL)
return NULL; return ERR_PTR(err);
rwlock_init(&ndev->lock); rwlock_init(&ndev->lock);
ndev->dev = dev; ndev->dev = dev;
...@@ -332,7 +333,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *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); ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
if (ndev->nd_parms == NULL) { if (ndev->nd_parms == NULL) {
kfree(ndev); kfree(ndev);
return NULL; return ERR_PTR(err);
} }
if (ndev->cnf.forwarding) if (ndev->cnf.forwarding)
dev_disable_lro(dev); dev_disable_lro(dev);
...@@ -346,17 +347,14 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) ...@@ -346,17 +347,14 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
neigh_parms_release(&nd_tbl, ndev->nd_parms); neigh_parms_release(&nd_tbl, ndev->nd_parms);
dev_put(dev); dev_put(dev);
kfree(ndev); kfree(ndev);
return NULL; return ERR_PTR(err);
} }
if (snmp6_register_dev(ndev) < 0) { if (snmp6_register_dev(ndev) < 0) {
ADBG(KERN_WARNING ADBG(KERN_WARNING
"%s: cannot create /proc/net/dev_snmp6/%s\n", "%s: cannot create /proc/net/dev_snmp6/%s\n",
__func__, dev->name); __func__, dev->name);
neigh_parms_release(&nd_tbl, ndev->nd_parms); goto err_release;
ndev->dead = 1;
in6_dev_finish_destroy(ndev);
return NULL;
} }
/* One reference from device. We must do this before /* One reference from device. We must do this before
...@@ -394,7 +392,12 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) ...@@ -394,7 +392,12 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
ipv6_mc_init_dev(ndev); ipv6_mc_init_dev(ndev);
ndev->tstamp = jiffies; 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 */ /* protected by rtnl_lock */
rcu_assign_pointer(dev->ip6_ptr, ndev); rcu_assign_pointer(dev->ip6_ptr, ndev);
...@@ -409,6 +412,12 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) ...@@ -409,6 +412,12 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
return ndev; 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) 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) ...@@ -420,7 +429,7 @@ static struct inet6_dev *ipv6_find_idev(struct net_device *dev)
idev = __in6_dev_get(dev); idev = __in6_dev_get(dev);
if (!idev) { if (!idev) {
idev = ipv6_add_dev(dev); idev = ipv6_add_dev(dev);
if (!idev) if (IS_ERR(idev))
return NULL; return NULL;
} }
...@@ -2830,8 +2839,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, ...@@ -2830,8 +2839,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
case NETDEV_REGISTER: case NETDEV_REGISTER:
if (!idev && dev->mtu >= IPV6_MIN_MTU) { if (!idev && dev->mtu >= IPV6_MIN_MTU) {
idev = ipv6_add_dev(dev); idev = ipv6_add_dev(dev);
if (!idev) if (IS_ERR(idev))
return notifier_from_errno(-ENOMEM); return notifier_from_errno(PTR_ERR(idev));
} }
break; break;
...@@ -2851,7 +2860,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, ...@@ -2851,7 +2860,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
if (!idev && dev->mtu >= IPV6_MIN_MTU) if (!idev && dev->mtu >= IPV6_MIN_MTU)
idev = ipv6_add_dev(dev); idev = ipv6_add_dev(dev);
if (idev) { if (!IS_ERR_OR_NULL(idev)) {
idev->if_flags |= IF_READY; idev->if_flags |= IF_READY;
run_pending = 1; run_pending = 1;
} }
...@@ -2894,7 +2903,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, ...@@ -2894,7 +2903,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
break; break;
} }
if (idev) { if (!IS_ERR_OR_NULL(idev)) {
if (run_pending) if (run_pending)
addrconf_dad_run(idev); addrconf_dad_run(idev);
...@@ -2929,7 +2938,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, ...@@ -2929,7 +2938,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
if (!idev && dev->mtu >= IPV6_MIN_MTU) { if (!idev && dev->mtu >= IPV6_MIN_MTU) {
idev = ipv6_add_dev(dev); idev = ipv6_add_dev(dev);
if (idev) if (!IS_ERR(idev))
break; break;
} }
...@@ -2950,10 +2959,14 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, ...@@ -2950,10 +2959,14 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
if (idev) { if (idev) {
snmp6_unregister_dev(idev); snmp6_unregister_dev(idev);
addrconf_sysctl_unregister(idev); addrconf_sysctl_unregister(idev);
addrconf_sysctl_register(idev); err = addrconf_sysctl_register(idev);
err = snmp6_register_dev(idev);
if (err) if (err)
return notifier_from_errno(err); return notifier_from_errno(err);
err = snmp6_register_dev(idev);
if (err) {
addrconf_sysctl_unregister(idev);
return notifier_from_errno(err);
}
} }
break; break;
...@@ -5248,12 +5261,23 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p) ...@@ -5248,12 +5261,23 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p)
kfree(t); 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); &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); idev, &idev->cnf);
if (err)
neigh_sysctl_unregister(idev->nd_parms);
return err;
} }
static void addrconf_sysctl_unregister(struct inet6_dev *idev) static void addrconf_sysctl_unregister(struct inet6_dev *idev)
...@@ -5338,6 +5362,7 @@ static struct rtnl_af_ops inet6_ops = { ...@@ -5338,6 +5362,7 @@ static struct rtnl_af_ops inet6_ops = {
int __init addrconf_init(void) int __init addrconf_init(void)
{ {
struct inet6_dev *idev;
int i, err; int i, err;
err = ipv6_addr_label_init(); err = ipv6_addr_label_init();
...@@ -5376,11 +5401,12 @@ int __init addrconf_init(void) ...@@ -5376,11 +5401,12 @@ int __init addrconf_init(void)
* device and it being up should be removed. * device and it being up should be removed.
*/ */
rtnl_lock(); rtnl_lock();
if (!ipv6_add_dev(init_net.loopback_dev)) idev = ipv6_add_dev(init_net.loopback_dev);
err = -ENOMEM;
rtnl_unlock(); rtnl_unlock();
if (err) if (IS_ERR(idev)) {
err = PTR_ERR(idev);
goto errlo; goto errlo;
}
for (i = 0; i < IN6_ADDR_HSIZE; i++) for (i = 0; i < IN6_ADDR_HSIZE; i++)
INIT_HLIST_HEAD(&inet6_addr_lst[i]); INIT_HLIST_HEAD(&inet6_addr_lst[i]);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment