]> err.no Git - linux-2.6/blobdiff - net/core/dev.c
[MAC80211]: fix race conditions with keys
[linux-2.6] / net / core / dev.c
index 3a3d5ee739098b66f4a84ca0d708aff275a3b64a..666c112efb55fc6bd76fd7f073dca951665952da 100644 (file)
@@ -208,6 +208,34 @@ static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex)
        return &net->dev_index_head[ifindex & ((1 << NETDEV_HASHBITS) - 1)];
 }
 
+/* Device list insertion */
+static int list_netdevice(struct net_device *dev)
+{
+       struct net *net = dev->nd_net;
+
+       ASSERT_RTNL();
+
+       write_lock_bh(&dev_base_lock);
+       list_add_tail(&dev->dev_list, &net->dev_base_head);
+       hlist_add_head(&dev->name_hlist, dev_name_hash(net, dev->name));
+       hlist_add_head(&dev->index_hlist, dev_index_hash(net, dev->ifindex));
+       write_unlock_bh(&dev_base_lock);
+       return 0;
+}
+
+/* Device list removal */
+static void unlist_netdevice(struct net_device *dev)
+{
+       ASSERT_RTNL();
+
+       /* Unlink dev from the device chain */
+       write_lock_bh(&dev_base_lock);
+       list_del(&dev->dev_list);
+       hlist_del(&dev->name_hlist);
+       hlist_del(&dev->index_hlist);
+       write_unlock_bh(&dev_base_lock);
+}
+
 /*
  *     Our notifier list
  */
@@ -739,9 +767,10 @@ int dev_valid_name(const char *name)
 }
 
 /**
- *     dev_alloc_name - allocate a name for a device
- *     @dev: device
+ *     __dev_alloc_name - allocate a name for a device
+ *     @net: network namespace to allocate the device name in
  *     @name: name format string
+ *     @buf:  scratch buffer and result name string
  *
  *     Passed a format string - eg "lt%d" it will try and find a suitable
  *     id. It scans list of devices to build up a free map, then chooses
@@ -752,18 +781,13 @@ int dev_valid_name(const char *name)
  *     Returns the number of the unit assigned or a negative errno code.
  */
 
-int dev_alloc_name(struct net_device *dev, const char *name)
+static int __dev_alloc_name(struct net *net, const char *name, char *buf)
 {
        int i = 0;
-       char buf[IFNAMSIZ];
        const char *p;
        const int max_netdevices = 8*PAGE_SIZE;
        long *inuse;
        struct net_device *d;
-       struct net *net;
-
-       BUG_ON(!dev->nd_net);
-       net = dev->nd_net;
 
        p = strnchr(name, IFNAMSIZ-1, '%');
        if (p) {
@@ -787,7 +811,7 @@ int dev_alloc_name(struct net_device *dev, const char *name)
                                continue;
 
                        /*  avoid cases where sscanf is not exact inverse of printf */
-                       snprintf(buf, sizeof(buf), name, i);
+                       snprintf(buf, IFNAMSIZ, name, i);
                        if (!strncmp(buf, d->name, IFNAMSIZ))
                                set_bit(i, inuse);
                }
@@ -796,11 +820,9 @@ int dev_alloc_name(struct net_device *dev, const char *name)
                free_page((unsigned long) inuse);
        }
 
-       snprintf(buf, sizeof(buf), name, i);
-       if (!__dev_get_by_name(net, buf)) {
-               strlcpy(dev->name, buf, IFNAMSIZ);
+       snprintf(buf, IFNAMSIZ, name, i);
+       if (!__dev_get_by_name(net, buf))
                return i;
-       }
 
        /* It is possible to run out of possible slots
         * when the name is long and there isn't enough space left
@@ -809,6 +831,34 @@ int dev_alloc_name(struct net_device *dev, const char *name)
        return -ENFILE;
 }
 
+/**
+ *     dev_alloc_name - allocate a name for a device
+ *     @dev: device
+ *     @name: name format string
+ *
+ *     Passed a format string - eg "lt%d" it will try and find a suitable
+ *     id. It scans list of devices to build up a free map, then chooses
+ *     the first empty slot. The caller must hold the dev_base or rtnl lock
+ *     while allocating the name and adding the device in order to avoid
+ *     duplicates.
+ *     Limited to bits_per_byte * page size devices (ie 32K on most platforms).
+ *     Returns the number of the unit assigned or a negative errno code.
+ */
+
+int dev_alloc_name(struct net_device *dev, const char *name)
+{
+       char buf[IFNAMSIZ];
+       struct net *net;
+       int ret;
+
+       BUG_ON(!dev->nd_net);
+       net = dev->nd_net;
+       ret = __dev_alloc_name(net, name, buf);
+       if (ret >= 0)
+               strlcpy(dev->name, buf, IFNAMSIZ);
+       return ret;
+}
+
 
 /**
  *     dev_change_name - change name of a device
@@ -1010,6 +1060,8 @@ int dev_open(struct net_device *dev)
  */
 int dev_close(struct net_device *dev)
 {
+       might_sleep();
+
        if (!(dev->flags & IFF_UP))
                return 0;
 
@@ -2412,7 +2464,11 @@ static int dev_seq_open(struct inode *inode, struct file *file)
        res =  seq_open(file, &dev_seq_ops);
        if (!res) {
                seq = file->private_data;
-               seq->private = get_net(PROC_NET(inode));
+               seq->private = get_proc_net(inode);
+               if (!seq->private) {
+                       seq_release(inode, file);
+                       res = -ENXIO;
+               }
        }
        return res;
 }
@@ -3549,12 +3605,8 @@ int register_netdevice(struct net_device *dev)
        set_bit(__LINK_STATE_PRESENT, &dev->state);
 
        dev_init_scheduler(dev);
-       write_lock_bh(&dev_base_lock);
-       list_add_tail(&dev->dev_list, &net->dev_base_head);
-       hlist_add_head(&dev->name_hlist, head);
-       hlist_add_head(&dev->index_hlist, dev_index_hash(net, dev->ifindex));
        dev_hold(dev);
-       write_unlock_bh(&dev_base_lock);
+       list_netdevice(dev);
 
        /* Notify protocols, that a new device appeared. */
        ret = raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
@@ -3861,11 +3913,7 @@ void unregister_netdevice(struct net_device *dev)
                dev_close(dev);
 
        /* And unlink it from device chain. */
-       write_lock_bh(&dev_base_lock);
-       list_del(&dev->dev_list);
-       hlist_del(&dev->name_hlist);
-       hlist_del(&dev->index_hlist);
-       write_unlock_bh(&dev_base_lock);
+       unlist_netdevice(dev);
 
        dev->reg_state = NETREG_UNREGISTERING;
 
@@ -3923,6 +3971,122 @@ void unregister_netdev(struct net_device *dev)
 
 EXPORT_SYMBOL(unregister_netdev);
 
+/**
+ *     dev_change_net_namespace - move device to different nethost namespace
+ *     @dev: device
+ *     @net: network namespace
+ *     @pat: If not NULL name pattern to try if the current device name
+ *           is already taken in the destination network namespace.
+ *
+ *     This function shuts down a device interface and moves it
+ *     to a new network namespace. On success 0 is returned, on
+ *     a failure a netagive errno code is returned.
+ *
+ *     Callers must hold the rtnl semaphore.
+ */
+
+int dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat)
+{
+       char buf[IFNAMSIZ];
+       const char *destname;
+       int err;
+
+       ASSERT_RTNL();
+
+       /* Don't allow namespace local devices to be moved. */
+       err = -EINVAL;
+       if (dev->features & NETIF_F_NETNS_LOCAL)
+               goto out;
+
+       /* Ensure the device has been registrered */
+       err = -EINVAL;
+       if (dev->reg_state != NETREG_REGISTERED)
+               goto out;
+
+       /* Get out if there is nothing todo */
+       err = 0;
+       if (dev->nd_net == net)
+               goto out;
+
+       /* Pick the destination device name, and ensure
+        * we can use it in the destination network namespace.
+        */
+       err = -EEXIST;
+       destname = dev->name;
+       if (__dev_get_by_name(net, destname)) {
+               /* We get here if we can't use the current device name */
+               if (!pat)
+                       goto out;
+               if (!dev_valid_name(pat))
+                       goto out;
+               if (strchr(pat, '%')) {
+                       if (__dev_alloc_name(net, pat, buf) < 0)
+                               goto out;
+                       destname = buf;
+               } else
+                       destname = pat;
+               if (__dev_get_by_name(net, destname))
+                       goto out;
+       }
+
+       /*
+        * And now a mini version of register_netdevice unregister_netdevice.
+        */
+
+       /* If device is running close it first. */
+       if (dev->flags & IFF_UP)
+               dev_close(dev);
+
+       /* And unlink it from device chain */
+       err = -ENODEV;
+       unlist_netdevice(dev);
+
+       synchronize_net();
+
+       /* Shutdown queueing discipline. */
+       dev_shutdown(dev);
+
+       /* Notify protocols, that we are about to destroy
+          this device. They should clean all the things.
+       */
+       call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
+
+       /*
+        *      Flush the unicast and multicast chains
+        */
+       dev_addr_discard(dev);
+
+       /* Actually switch the network namespace */
+       dev->nd_net = net;
+
+       /* Assign the new device name */
+       if (destname != dev->name)
+               strcpy(dev->name, destname);
+
+       /* If there is an ifindex conflict assign a new one */
+       if (__dev_get_by_index(net, dev->ifindex)) {
+               int iflink = (dev->iflink == dev->ifindex);
+               dev->ifindex = dev_new_index(net);
+               if (iflink)
+                       dev->iflink = dev->ifindex;
+       }
+
+       /* Fixup sysfs */
+       err = device_rename(&dev->dev, dev->name);
+       BUG_ON(err);
+
+       /* Add the device back in the hashes */
+       list_netdevice(dev);
+
+       /* Notify protocols, that a new device appeared. */
+       call_netdevice_notifiers(NETDEV_REGISTER, dev);
+
+       synchronize_net();
+       err = 0;
+out:
+       return err;
+}
+
 static int dev_cpu_callback(struct notifier_block *nfb,
                            unsigned long action,
                            void *ocpu)
@@ -4155,6 +4319,36 @@ static struct pernet_operations netdev_net_ops = {
        .exit = netdev_exit,
 };
 
+static void default_device_exit(struct net *net)
+{
+       struct net_device *dev, *next;
+       /*
+        * Push all migratable of the network devices back to the
+        * initial network namespace
+        */
+       rtnl_lock();
+       for_each_netdev_safe(net, dev, next) {
+               int err;
+
+               /* Ignore unmoveable devices (i.e. loopback) */
+               if (dev->features & NETIF_F_NETNS_LOCAL)
+                       continue;
+
+               /* Push remaing network devices to init_net */
+               err = dev_change_net_namespace(dev, &init_net, "dev%d");
+               if (err) {
+                       printk(KERN_WARNING "%s: failed to move %s to init_net: %d\n",
+                               __func__, dev->name, err);
+                       unregister_netdevice(dev);
+               }
+       }
+       rtnl_unlock();
+}
+
+static struct pernet_operations default_device_ops = {
+       .exit = default_device_exit,
+};
+
 /*
  *     Initialize the DEV module. At boot time this walks the device list and
  *     unhooks any devices that fail to initialise (normally hardware not
@@ -4185,6 +4379,9 @@ static int __init net_dev_init(void)
        if (register_pernet_subsys(&netdev_net_ops))
                goto out;
 
+       if (register_pernet_device(&default_device_ops))
+               goto out;
+
        /*
         *      Initialise the packet receive queues.
         */