nfsd: nfsd should drop CAP_MKNOD for non-root
[safe/jmp/linux-2.6] / net / wireless / wext.c
index dabc645..cb6a5bb 100644 (file)
@@ -64,7 +64,7 @@
  *     o Remove spy_offset from struct iw_handler_def
  *     o Start deprecating dev->get_wireless_stats, output a warning
  *     o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
- *     o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
+ *     o Don't lose INVALID/DBM flags when clearing UPDATED flags (iwstats)
  *
  * v8 - 17.02.06 - Jean II
  *     o RtNetlink requests support (SET/GET)
@@ -93,6 +93,7 @@
 #include <linux/if_arp.h>              /* ARPHRD_ETHER */
 #include <linux/etherdevice.h>         /* compare_ether_addr */
 #include <linux/interrupt.h>
+#include <net/net_namespace.h>
 
 #include <linux/wireless.h>            /* Pretty obvious */
 #include <net/iw_handler.h>            /* New driver API */
@@ -416,20 +417,6 @@ static const int event_type_size[] = {
        IW_EV_QUAL_LEN,                 /* IW_HEADER_TYPE_QUAL */
 };
 
-/* Size (in bytes) of various events, as packed */
-static const int event_type_pk_size[] = {
-       IW_EV_LCP_PK_LEN,               /* IW_HEADER_TYPE_NULL */
-       0,
-       IW_EV_CHAR_PK_LEN,              /* IW_HEADER_TYPE_CHAR */
-       0,
-       IW_EV_UINT_PK_LEN,              /* IW_HEADER_TYPE_UINT */
-       IW_EV_FREQ_PK_LEN,              /* IW_HEADER_TYPE_FREQ */
-       IW_EV_ADDR_PK_LEN,              /* IW_HEADER_TYPE_ADDR */
-       0,
-       IW_EV_POINT_PK_LEN,             /* Without variable payload */
-       IW_EV_PARAM_PK_LEN,             /* IW_HEADER_TYPE_PARAM */
-       IW_EV_QUAL_PK_LEN,              /* IW_HEADER_TYPE_QUAL */
-};
 
 /************************ COMMON SUBROUTINES ************************/
 /*
@@ -440,10 +427,8 @@ static const int event_type_pk_size[] = {
 /* ---------------------------------------------------------------- */
 /*
  * Return the driver handler associated with a specific Wireless Extension.
- * Called from various place, so make sure it remains efficient.
  */
-static inline iw_handler get_handler(struct net_device *dev,
-                                    unsigned int cmd)
+static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
 {
        /* Don't "optimise" the following variable, it will crash */
        unsigned int    index;          /* *MUST* be unsigned */
@@ -470,7 +455,7 @@ static inline iw_handler get_handler(struct net_device *dev,
 /*
  * Get statistics out of the driver
  */
-static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
+static struct iw_statistics *get_wireless_stats(struct net_device *dev)
 {
        /* New location */
        if ((dev->wireless_handlers != NULL) &&
@@ -478,7 +463,7 @@ static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
                return dev->wireless_handlers->get_wireless_stats(dev);
 
        /* Not found */
-       return (struct iw_statistics *) NULL;
+       return NULL;
 }
 
 /* ---------------------------------------------------------------- */
@@ -500,14 +485,14 @@ static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
  * netif_running(dev) test. I'm open on that one...
  * Hopefully, the driver will remember to do a commit in "open()" ;-)
  */
-static inline int call_commit_handler(struct net_device *      dev)
+static int call_commit_handler(struct net_device *dev)
 {
        if ((netif_running(dev)) &&
-          (dev->wireless_handlers->standard[0] != NULL)) {
+          (dev->wireless_handlers->standard[0] != NULL))
                /* Call the commit handler on the driver */
                return dev->wireless_handlers->standard[0](dev, NULL,
                                                           NULL, NULL);
-       else
+       else
                return 0;               /* Command completed successfully */
 }
 
@@ -515,7 +500,7 @@ static inline int call_commit_handler(struct net_device *   dev)
 /*
  * Calculate size of private arguments
  */
-static inline int get_priv_size(__u16  args)
+static int get_priv_size(__u16 args)
 {
        int     num = args & IW_PRIV_SIZE_MASK;
        int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
@@ -527,10 +512,9 @@ static inline int get_priv_size(__u16      args)
 /*
  * Re-calculate the size of private arguments
  */
-static inline int adjust_priv_size(__u16               args,
-                                  union iwreq_data *   wrqu)
+static int adjust_priv_size(__u16 args, struct iw_point *iwp)
 {
-       int     num = wrqu->data.length;
+       int     num = iwp->length;
        int     max = args & IW_PRIV_SIZE_MASK;
        int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
 
@@ -556,8 +540,7 @@ static int iw_handler_get_iwstats(struct net_device *               dev,
        struct iw_statistics *stats;
 
        stats = get_wireless_stats(dev);
-       if (stats != (struct iw_statistics *) NULL) {
-
+       if (stats) {
                /* Copy statistics to extra */
                memcpy(extra, stats, sizeof(struct iw_statistics));
                wrqu->data.length = sizeof(struct iw_statistics);
@@ -622,8 +605,8 @@ static int iw_handler_get_private(struct net_device *               dev,
 /*
  * Print one entry (line) of /proc/net/wireless
  */
-static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
-                                                struct net_device *dev)
+static void wireless_seq_printf_stats(struct seq_file *seq,
+                                     struct net_device *dev)
 {
        /* Get stats from the driver */
        struct iw_statistics *stats = get_wireless_stats(dev);
@@ -675,7 +658,8 @@ static const struct seq_operations wireless_seq_ops = {
 
 static int wireless_seq_open(struct inode *inode, struct file *file)
 {
-       return seq_open(file, &wireless_seq_ops);
+       return seq_open_net(inode, file, &wireless_seq_ops,
+                           sizeof(struct seq_net_private));
 }
 
 static const struct file_operations wireless_seq_fops = {
@@ -683,17 +667,22 @@ static const struct file_operations wireless_seq_fops = {
        .open    = wireless_seq_open,
        .read    = seq_read,
        .llseek  = seq_lseek,
-       .release = seq_release,
+       .release = seq_release_net,
 };
 
-int __init wext_proc_init(void)
+int wext_proc_init(struct net *net)
 {
        /* Create /proc/net/wireless entry */
-       if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
+       if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops))
                return -ENOMEM;
 
        return 0;
 }
+
+void wext_proc_exit(struct net *net)
+{
+       proc_net_remove(net, "wireless");
+}
 #endif /* CONFIG_PROC_FS */
 
 /************************** IOCTL SUPPORT **************************/
@@ -705,19 +694,150 @@ int __init wext_proc_init(void)
  */
 
 /* ---------------------------------------------------------------- */
+static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd,
+                                  const struct iw_ioctl_description *descr,
+                                  iw_handler handler, struct net_device *dev,
+                                  struct iw_request_info *info)
+{
+       int err, extra_size, user_length = 0, essid_compat = 0;
+       char *extra;
+
+       /* Calculate space needed by arguments. Always allocate
+        * for max space.
+        */
+       extra_size = descr->max_tokens * descr->token_size;
+
+       /* Check need for ESSID compatibility for WE < 21 */
+       switch (cmd) {
+       case SIOCSIWESSID:
+       case SIOCGIWESSID:
+       case SIOCSIWNICKN:
+       case SIOCGIWNICKN:
+               if (iwp->length == descr->max_tokens + 1)
+                       essid_compat = 1;
+               else if (IW_IS_SET(cmd) && (iwp->length != 0)) {
+                       char essid[IW_ESSID_MAX_SIZE + 1];
+
+                       err = copy_from_user(essid, iwp->pointer,
+                                            iwp->length *
+                                            descr->token_size);
+                       if (err)
+                               return -EFAULT;
+
+                       if (essid[iwp->length - 1] == '\0')
+                               essid_compat = 1;
+               }
+               break;
+       default:
+               break;
+       }
+
+       iwp->length -= essid_compat;
+
+       /* Check what user space is giving us */
+       if (IW_IS_SET(cmd)) {
+               /* Check NULL pointer */
+               if (!iwp->pointer && iwp->length != 0)
+                       return -EFAULT;
+               /* Check if number of token fits within bounds */
+               if (iwp->length > descr->max_tokens)
+                       return -E2BIG;
+               if (iwp->length < descr->min_tokens)
+                       return -EINVAL;
+       } else {
+               /* Check NULL pointer */
+               if (!iwp->pointer)
+                       return -EFAULT;
+               /* Save user space buffer size for checking */
+               user_length = iwp->length;
+
+               /* Don't check if user_length > max to allow forward
+                * compatibility. The test user_length < min is
+                * implied by the test at the end.
+                */
+
+               /* Support for very large requests */
+               if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+                   (user_length > descr->max_tokens)) {
+                       /* Allow userspace to GET more than max so
+                        * we can support any size GET requests.
+                        * There is still a limit : -ENOMEM.
+                        */
+                       extra_size = user_length * descr->token_size;
+
+                       /* Note : user_length is originally a __u16,
+                        * and token_size is controlled by us,
+                        * so extra_size won't get negative and
+                        * won't overflow...
+                        */
+               }
+       }
+
+       /* kzalloc() ensures NULL-termination for essid_compat. */
+       extra = kzalloc(extra_size, GFP_KERNEL);
+       if (!extra)
+               return -ENOMEM;
+
+       /* If it is a SET, get all the extra data in here */
+       if (IW_IS_SET(cmd) && (iwp->length != 0)) {
+               if (copy_from_user(extra, iwp->pointer,
+                                  iwp->length *
+                                  descr->token_size)) {
+                       err = -EFAULT;
+                       goto out;
+               }
+       }
+
+       err = handler(dev, info, (union iwreq_data *) iwp, extra);
+
+       iwp->length += essid_compat;
+
+       /* If we have something to return to the user */
+       if (!err && IW_IS_GET(cmd)) {
+               /* Check if there is enough buffer up there */
+               if (user_length < iwp->length) {
+                       err = -E2BIG;
+                       goto out;
+               }
+
+               if (copy_to_user(iwp->pointer, extra,
+                                iwp->length *
+                                descr->token_size)) {
+                       err = -EFAULT;
+                       goto out;
+               }
+       }
+
+       /* Generate an event to notify listeners of the change */
+       if ((descr->flags & IW_DESCR_FLAG_EVENT) && err == -EIWCOMMIT) {
+               union iwreq_data *data = (union iwreq_data *) iwp;
+
+               if (descr->flags & IW_DESCR_FLAG_RESTRICT)
+                       /* If the event is restricted, don't
+                        * export the payload.
+                        */
+                       wireless_send_event(dev, cmd, data, NULL);
+               else
+                       wireless_send_event(dev, cmd, data, extra);
+       }
+
+out:
+       kfree(extra);
+       return err;
+}
+
 /*
  * Wrapper to call a standard Wireless Extension handler.
  * We do various checks and also take care of moving data between
  * user space and kernel space.
  */
 static int ioctl_standard_call(struct net_device *     dev,
-                              struct ifreq *           ifr,
+                              struct iwreq             *iwr,
                               unsigned int             cmd,
+                              struct iw_request_info   *info,
                               iw_handler               handler)
 {
-       struct iwreq *                          iwr = (struct iwreq *) ifr;
        const struct iw_ioctl_description *     descr;
-       struct iw_request_info                  info;
        int                                     ret = -EINVAL;
 
        /* Get the description of the IOCTL */
@@ -725,146 +845,19 @@ static int ioctl_standard_call(struct net_device *       dev,
                return -EOPNOTSUPP;
        descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
 
-       /* Prepare the call */
-       info.cmd = cmd;
-       info.flags = 0;
-
        /* Check if we have a pointer to user space data or not */
        if (descr->header_type != IW_HEADER_TYPE_POINT) {
 
                /* No extra arguments. Trivial to handle */
-               ret = handler(dev, &info, &(iwr->u), NULL);
+               ret = handler(dev, info, &(iwr->u), NULL);
 
                /* Generate an event to notify listeners of the change */
                if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
                   ((ret == 0) || (ret == -EIWCOMMIT)))
                        wireless_send_event(dev, cmd, &(iwr->u), NULL);
        } else {
-               char *  extra;
-               int     extra_size;
-               int     user_length = 0;
-               int     err;
-               int     essid_compat = 0;
-
-               /* Calculate space needed by arguments. Always allocate
-                * for max space. Easier, and won't last long... */
-               extra_size = descr->max_tokens * descr->token_size;
-
-               /* Check need for ESSID compatibility for WE < 21 */
-               switch (cmd) {
-               case SIOCSIWESSID:
-               case SIOCGIWESSID:
-               case SIOCSIWNICKN:
-               case SIOCGIWNICKN:
-                       if (iwr->u.data.length == descr->max_tokens + 1)
-                               essid_compat = 1;
-                       else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-                               char essid[IW_ESSID_MAX_SIZE + 1];
-
-                               err = copy_from_user(essid, iwr->u.data.pointer,
-                                                    iwr->u.data.length *
-                                                    descr->token_size);
-                               if (err)
-                                       return -EFAULT;
-
-                               if (essid[iwr->u.data.length - 1] == '\0')
-                                       essid_compat = 1;
-                       }
-                       break;
-               default:
-                       break;
-               }
-
-               iwr->u.data.length -= essid_compat;
-
-               /* Check what user space is giving us */
-               if (IW_IS_SET(cmd)) {
-                       /* Check NULL pointer */
-                       if ((iwr->u.data.pointer == NULL) &&
-                          (iwr->u.data.length != 0))
-                               return -EFAULT;
-                       /* Check if number of token fits within bounds */
-                       if (iwr->u.data.length > descr->max_tokens)
-                               return -E2BIG;
-                       if (iwr->u.data.length < descr->min_tokens)
-                               return -EINVAL;
-               } else {
-                       /* Check NULL pointer */
-                       if (iwr->u.data.pointer == NULL)
-                               return -EFAULT;
-                       /* Save user space buffer size for checking */
-                       user_length = iwr->u.data.length;
-
-                       /* Don't check if user_length > max to allow forward
-                        * compatibility. The test user_length < min is
-                        * implied by the test at the end. */
-
-                       /* Support for very large requests */
-                       if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
-                          (user_length > descr->max_tokens)) {
-                               /* Allow userspace to GET more than max so
-                                * we can support any size GET requests.
-                                * There is still a limit : -ENOMEM. */
-                               extra_size = user_length * descr->token_size;
-                               /* Note : user_length is originally a __u16,
-                                * and token_size is controlled by us,
-                                * so extra_size won't get negative and
-                                * won't overflow... */
-                       }
-               }
-
-               /* Create the kernel buffer */
-               /*    kzalloc ensures NULL-termination for essid_compat */
-               extra = kzalloc(extra_size, GFP_KERNEL);
-               if (extra == NULL) {
-                       return -ENOMEM;
-               }
-
-               /* If it is a SET, get all the extra data in here */
-               if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-                       err = copy_from_user(extra, iwr->u.data.pointer,
-                                            iwr->u.data.length *
-                                            descr->token_size);
-                       if (err) {
-                               kfree(extra);
-                               return -EFAULT;
-                       }
-               }
-
-               /* Call the handler */
-               ret = handler(dev, &info, &(iwr->u), extra);
-
-               iwr->u.data.length += essid_compat;
-
-               /* If we have something to return to the user */
-               if (!ret && IW_IS_GET(cmd)) {
-                       /* Check if there is enough buffer up there */
-                       if (user_length < iwr->u.data.length) {
-                               kfree(extra);
-                               return -E2BIG;
-                       }
-
-                       err = copy_to_user(iwr->u.data.pointer, extra,
-                                          iwr->u.data.length *
-                                          descr->token_size);
-                       if (err)
-                               ret =  -EFAULT;
-               }
-
-               /* Generate an event to notify listeners of the change */
-               if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
-                  ((ret == 0) || (ret == -EIWCOMMIT))) {
-                       if (descr->flags & IW_DESCR_FLAG_RESTRICT)
-                               /* If the event is restricted, don't
-                                * export the payload */
-                               wireless_send_event(dev, cmd, &(iwr->u), NULL);
-                       else
-                               wireless_send_event(dev, cmd, &(iwr->u),
-                                                   extra);
-               }
-
-               /* Cleanup - I told you it wasn't that long ;-) */
-               kfree(extra);
+               ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr,
+                                             handler, dev, info);
        }
 
        /* Call commit handler if needed and defined */
@@ -892,27 +885,22 @@ static int ioctl_standard_call(struct net_device *        dev,
  * a iw_handler but process it in your ioctl handler (i.e. use the
  * old driver API).
  */
-static inline int ioctl_private_call(struct net_device *       dev,
-                                    struct ifreq *             ifr,
-                                    unsigned int               cmd,
-                                    iw_handler         handler)
+static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd,
+                                  const struct iw_priv_args **descrp)
 {
-       struct iwreq *                  iwr = (struct iwreq *) ifr;
-       const struct iw_priv_args *     descr = NULL;
-       struct iw_request_info          info;
-       int                             extra_size = 0;
-       int                             i;
-       int                             ret = -EINVAL;
+       const struct iw_priv_args *descr;
+       int i, extra_size;
 
-       /* Get the description of the IOCTL */
-       for (i = 0; i < dev->wireless_handlers->num_private_args; i++)
+       descr = NULL;
+       for (i = 0; i < dev->wireless_handlers->num_private_args; i++) {
                if (cmd == dev->wireless_handlers->private_args[i].cmd) {
-                       descr = &(dev->wireless_handlers->private_args[i]);
+                       descr = &dev->wireless_handlers->private_args[i];
                        break;
                }
+       }
 
-       /* Compute the size of the set/get arguments */
-       if (descr != NULL) {
+       extra_size = 0;
+       if (descr) {
                if (IW_IS_SET(cmd)) {
                        int     offset = 0;     /* For sub-ioctls */
                        /* Check for sub-ioctl handler */
@@ -937,76 +925,77 @@ static inline int ioctl_private_call(struct net_device *  dev,
                                extra_size = 0;
                }
        }
+       *descrp = descr;
+       return extra_size;
+}
 
-       /* Prepare the call */
-       info.cmd = cmd;
-       info.flags = 0;
+static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd,
+                                 const struct iw_priv_args *descr,
+                                 iw_handler handler, struct net_device *dev,
+                                 struct iw_request_info *info, int extra_size)
+{
+       char *extra;
+       int err;
 
-       /* Check if we have a pointer to user space data or not. */
-       if (extra_size == 0) {
-               /* No extra arguments. Trivial to handle */
-               ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
-       } else {
-               char *  extra;
-               int     err;
+       /* Check what user space is giving us */
+       if (IW_IS_SET(cmd)) {
+               if (!iwp->pointer && iwp->length != 0)
+                       return -EFAULT;
 
-               /* Check what user space is giving us */
-               if (IW_IS_SET(cmd)) {
-                       /* Check NULL pointer */
-                       if ((iwr->u.data.pointer == NULL) &&
-                          (iwr->u.data.length != 0))
-                               return -EFAULT;
+               if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK))
+                       return -E2BIG;
+       } else if (!iwp->pointer)
+               return -EFAULT;
 
-                       /* Does it fits within bounds ? */
-                       if (iwr->u.data.length > (descr->set_args &
-                                                IW_PRIV_SIZE_MASK))
-                               return -E2BIG;
-               } else {
-                       /* Check NULL pointer */
-                       if (iwr->u.data.pointer == NULL)
-                               return -EFAULT;
-               }
+       extra = kmalloc(extra_size, GFP_KERNEL);
+       if (!extra)
+               return -ENOMEM;
 
-               /* Always allocate for max space. Easier, and won't last
-                * long... */
-               extra = kmalloc(extra_size, GFP_KERNEL);
-               if (extra == NULL) {
-                       return -ENOMEM;
+       /* If it is a SET, get all the extra data in here */
+       if (IW_IS_SET(cmd) && (iwp->length != 0)) {
+               if (copy_from_user(extra, iwp->pointer, extra_size)) {
+                       err = -EFAULT;
+                       goto out;
                }
+       }
 
-               /* If it is a SET, get all the extra data in here */
-               if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-                       err = copy_from_user(extra, iwr->u.data.pointer,
-                                            extra_size);
-                       if (err) {
-                               kfree(extra);
-                               return -EFAULT;
-                       }
-               }
+       /* Call the handler */
+       err = handler(dev, info, (union iwreq_data *) iwp, extra);
 
-               /* Call the handler */
-               ret = handler(dev, &info, &(iwr->u), extra);
+       /* If we have something to return to the user */
+       if (!err && IW_IS_GET(cmd)) {
+               /* Adjust for the actual length if it's variable,
+                * avoid leaking kernel bits outside.
+                */
+               if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
+                       extra_size = adjust_priv_size(descr->get_args, iwp);
 
-               /* If we have something to return to the user */
-               if (!ret && IW_IS_GET(cmd)) {
+               if (copy_to_user(iwp->pointer, extra, extra_size))
+                       err =  -EFAULT;
+       }
 
-                       /* Adjust for the actual length if it's variable,
-                        * avoid leaking kernel bits outside. */
-                       if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
-                               extra_size = adjust_priv_size(descr->get_args,
-                                                             &(iwr->u));
-                       }
+out:
+       kfree(extra);
+       return err;
+}
 
-                       err = copy_to_user(iwr->u.data.pointer, extra,
-                                          extra_size);
-                       if (err)
-                               ret =  -EFAULT;
-               }
+static int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
+                             unsigned int cmd, struct iw_request_info *info,
+                             iw_handler handler)
+{
+       int extra_size = 0, ret = -EINVAL;
+       const struct iw_priv_args *descr;
 
-               /* Cleanup - I told you it wasn't that long ;-) */
-               kfree(extra);
-       }
+       extra_size = get_priv_descr_and_size(dev, cmd, &descr);
 
+       /* Check if we have a pointer to user space data or not. */
+       if (extra_size == 0) {
+               /* No extra arguments. Trivial to handle */
+               ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
+       } else {
+               ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
+                                            handler, dev, info, extra_size);
+       }
 
        /* Call commit handler if needed and defined */
        if (ret == -EIWCOMMIT)
@@ -1016,12 +1005,21 @@ static inline int ioctl_private_call(struct net_device *        dev,
 }
 
 /* ---------------------------------------------------------------- */
+typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
+                              unsigned int, struct iw_request_info *,
+                              iw_handler);
+
 /*
  * Main IOCTl dispatcher.
  * Check the type of IOCTL and call the appropriate wrapper...
  */
-static int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
+static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
+                                 unsigned int cmd,
+                                 struct iw_request_info *info,
+                                 wext_ioctl_func standard,
+                                 wext_ioctl_func private)
 {
+       struct iwreq *iwr = (struct iwreq *) ifr;
        struct net_device *dev;
        iw_handler      handler;
 
@@ -1029,81 +1027,187 @@ static int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
         * The copy_to/from_user() of ifr is also dealt with in there */
 
        /* Make sure the device exist */
-       if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
+       if ((dev = __dev_get_by_name(net, ifr->ifr_name)) == NULL)
                return -ENODEV;
 
        /* A bunch of special cases, then the generic case...
         * Note that 'cmd' is already filtered in dev_ioctl() with
         * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
-       switch (cmd) {
-       case SIOCGIWSTATS:
-               /* Get Wireless Stats */
-               return ioctl_standard_call(dev,
-                                          ifr,
-                                          cmd,
-                                          &iw_handler_get_iwstats);
-
-       case SIOCGIWPRIV:
-               /* Check if we have some wireless handlers defined */
-               if (dev->wireless_handlers != NULL) {
-                       /* We export to user space the definition of
-                        * the private handler ourselves */
-                       return ioctl_standard_call(dev,
-                                                  ifr,
-                                                  cmd,
-                                                  &iw_handler_get_private);
-               }
-               // ## Fall-through for old API ##
-       default:
-               /* Generic IOCTL */
-               /* Basic check */
-               if (!netif_device_present(dev))
-                       return -ENODEV;
-               /* New driver API : try to find the handler */
-               handler = get_handler(dev, cmd);
-               if (handler != NULL) {
-                       /* Standard and private are not the same */
-                       if (cmd < SIOCIWFIRSTPRIV)
-                               return ioctl_standard_call(dev,
-                                                          ifr,
-                                                          cmd,
-                                                          handler);
-                       else
-                               return ioctl_private_call(dev,
-                                                         ifr,
-                                                         cmd,
-                                                         handler);
-               }
-               /* Old driver API : call driver ioctl handler */
-               if (dev->do_ioctl) {
-                       return dev->do_ioctl(dev, ifr, cmd);
-               }
-               return -EOPNOTSUPP;
+       if (cmd == SIOCGIWSTATS)
+               return standard(dev, iwr, cmd, info,
+                               &iw_handler_get_iwstats);
+
+       if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
+               return standard(dev, iwr, cmd, info,
+                               &iw_handler_get_private);
+
+       /* Basic check */
+       if (!netif_device_present(dev))
+               return -ENODEV;
+
+       /* New driver API : try to find the handler */
+       handler = get_handler(dev, cmd);
+       if (handler) {
+               /* Standard and private are not the same */
+               if (cmd < SIOCIWFIRSTPRIV)
+                       return standard(dev, iwr, cmd, info, handler);
+               else
+                       return private(dev, iwr, cmd, info, handler);
        }
-       /* Not reached */
-       return -EINVAL;
+       /* Old driver API : call driver ioctl handler */
+       if (dev->netdev_ops->ndo_do_ioctl)
+               return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
+       return -EOPNOTSUPP;
+}
+
+/* If command is `set a parameter', or `get the encoding parameters',
+ * check if the user has the right to do it.
+ */
+static int wext_permission_check(unsigned int cmd)
+{
+       if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT)
+           && !capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       return 0;
 }
 
 /* entry point from dev ioctl */
-int wext_handle_ioctl(struct ifreq *ifr, unsigned int cmd,
-                     void __user *arg)
+static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr,
+                              unsigned int cmd, struct iw_request_info *info,
+                              wext_ioctl_func standard,
+                              wext_ioctl_func private)
 {
-       int ret;
+       int ret = wext_permission_check(cmd);
+
+       if (ret)
+               return ret;
 
-       /* If command is `set a parameter', or
-        * `get the encoding parameters', check if
-        * the user has the right to do it */
-       if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT)
-               if (!capable(CAP_NET_ADMIN))
-                       return -EPERM;
-       dev_load(ifr->ifr_name);
+       dev_load(net, ifr->ifr_name);
        rtnl_lock();
-       ret = wireless_process_ioctl(ifr, cmd);
+       ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private);
        rtnl_unlock();
-       if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct ifreq)))
+
+       return ret;
+}
+
+int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
+                     void __user *arg)
+{
+       struct iw_request_info info = { .cmd = cmd, .flags = 0 };
+       int ret;
+
+       ret = wext_ioctl_dispatch(net, ifr, cmd, &info,
+                                 ioctl_standard_call,
+                                 ioctl_private_call);
+       if (ret >= 0 &&
+           IW_IS_GET(cmd) &&
+           copy_to_user(arg, ifr, sizeof(struct iwreq)))
+               return -EFAULT;
+
+       return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static int compat_standard_call(struct net_device      *dev,
+                               struct iwreq            *iwr,
+                               unsigned int            cmd,
+                               struct iw_request_info  *info,
+                               iw_handler              handler)
+{
+       const struct iw_ioctl_description *descr;
+       struct compat_iw_point *iwp_compat;
+       struct iw_point iwp;
+       int err;
+
+       descr = standard_ioctl + (cmd - SIOCIWFIRST);
+
+       if (descr->header_type != IW_HEADER_TYPE_POINT)
+               return ioctl_standard_call(dev, iwr, cmd, info, handler);
+
+       iwp_compat = (struct compat_iw_point *) &iwr->u.data;
+       iwp.pointer = compat_ptr(iwp_compat->pointer);
+       iwp.length = iwp_compat->length;
+       iwp.flags = iwp_compat->flags;
+
+       err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info);
+
+       iwp_compat->pointer = ptr_to_compat(iwp.pointer);
+       iwp_compat->length = iwp.length;
+       iwp_compat->flags = iwp.flags;
+
+       return err;
+}
+
+static int compat_private_call(struct net_device *dev, struct iwreq *iwr,
+                              unsigned int cmd, struct iw_request_info *info,
+                              iw_handler handler)
+{
+       const struct iw_priv_args *descr;
+       int ret, extra_size;
+
+       extra_size = get_priv_descr_and_size(dev, cmd, &descr);
+
+       /* Check if we have a pointer to user space data or not. */
+       if (extra_size == 0) {
+               /* No extra arguments. Trivial to handle */
+               ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
+       } else {
+               struct compat_iw_point *iwp_compat;
+               struct iw_point iwp;
+
+               iwp_compat = (struct compat_iw_point *) &iwr->u.data;
+               iwp.pointer = compat_ptr(iwp_compat->pointer);
+               iwp.length = iwp_compat->length;
+               iwp.flags = iwp_compat->flags;
+
+               ret = ioctl_private_iw_point(&iwp, cmd, descr,
+                                            handler, dev, info, extra_size);
+
+               iwp_compat->pointer = ptr_to_compat(iwp.pointer);
+               iwp_compat->length = iwp.length;
+               iwp_compat->flags = iwp.flags;
+       }
+
+       /* Call commit handler if needed and defined */
+       if (ret == -EIWCOMMIT)
+               ret = call_commit_handler(dev);
+
+       return ret;
+}
+
+int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
+                            unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       struct iw_request_info info;
+       struct iwreq iwr;
+       char *colon;
+       int ret;
+
+       if (copy_from_user(&iwr, argp, sizeof(struct iwreq)))
                return -EFAULT;
+
+       iwr.ifr_name[IFNAMSIZ-1] = 0;
+       colon = strchr(iwr.ifr_name, ':');
+       if (colon)
+               *colon = 0;
+
+       info.cmd = cmd;
+       info.flags = IW_REQUEST_FLAG_COMPAT;
+
+       ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, &info,
+                                 compat_standard_call,
+                                 compat_private_call);
+
+       if (ret >= 0 &&
+           IW_IS_GET(cmd) &&
+           copy_to_user(argp, &iwr, sizeof(struct iwreq)))
+               return -EFAULT;
+
        return ret;
 }
+#endif
 
 /************************* EVENT PROCESSING *************************/
 /*
@@ -1143,7 +1247,7 @@ static void wireless_nlevent_process(unsigned long data)
        struct sk_buff *skb;
 
        while ((skb = skb_dequeue(&wireless_nlevent_queue)))
-               rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
+               rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
 }
 
 static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
@@ -1155,18 +1259,17 @@ static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
  * current wireless config. Dumping the wireless config is far too
  * expensive (for each parameter, the driver need to query the hardware).
  */
-static inline int rtnetlink_fill_iwinfo(struct sk_buff *       skb,
-                                       struct net_device *     dev,
-                                       int                     type,
-                                       char *                  event,
-                                       int                     event_len)
+static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev,
+                                int type, char *event, int event_len)
 {
        struct ifinfomsg *r;
        struct nlmsghdr  *nlh;
-       unsigned char    *b = skb_tail_pointer(skb);
 
-       nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
-       r = NLMSG_DATA(nlh);
+       nlh = nlmsg_put(skb, 0, 0, type, sizeof(*r), 0);
+       if (nlh == NULL)
+               return -EMSGSIZE;
+
+       r = nlmsg_data(nlh);
        r->ifi_family = AF_UNSPEC;
        r->__ifi_pad = 0;
        r->ifi_type = dev->type;
@@ -1174,16 +1277,15 @@ static inline int rtnetlink_fill_iwinfo(struct sk_buff *        skb,
        r->ifi_flags = dev_get_flags(dev);
        r->ifi_change = 0;      /* Wireless changes don't affect those flags */
 
+       NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name);
        /* Add the wireless events in the netlink packet */
-       RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
+       NLA_PUT(skb, IFLA_WIRELESS, event_len, event);
 
-       nlh->nlmsg_len = skb_tail_pointer(skb) - b;
-       return skb->len;
+       return nlmsg_end(skb, nlh);
 
-nlmsg_failure:
-rtattr_failure:
-       nlmsg_trim(skb, b);
-       return -1;
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+       return -EMSGSIZE;
 }
 
 /* ---------------------------------------------------------------- */
@@ -1193,22 +1295,25 @@ rtattr_failure:
  * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
  * within a RTM_NEWLINK event.
  */
-static inline void rtmsg_iwinfo(struct net_device *    dev,
-                               char *                  event,
-                               int                     event_len)
+static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len)
 {
        struct sk_buff *skb;
-       int size = NLMSG_GOODSIZE;
+       int err;
+
+       if (!net_eq(dev_net(dev), &init_net))
+               return;
 
-       skb = alloc_skb(size, GFP_ATOMIC);
+       skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
        if (!skb)
                return;
 
-       if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
-                                 event, event_len) < 0) {
+       err = rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, event, event_len);
+       if (err < 0) {
+               WARN_ON(err == -EMSGSIZE);
                kfree_skb(skb);
                return;
        }
+
        NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
        skb_queue_tail(&wireless_nlevent_queue, skb);
        tasklet_schedule(&wireless_nlevent_tasklet);
@@ -1289,7 +1394,7 @@ void wireless_send_event(struct net_device *      dev,
        event->len = event_len;
        event->cmd = cmd;
        memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
-       if (extra != NULL)
+       if (extra)
                memcpy(((char *) event) + hdr_len, extra, extra_len);
 
        /* Send via the RtNetlink event channel */
@@ -1300,6 +1405,7 @@ void wireless_send_event(struct net_device *      dev,
 
        return;         /* Always success, I guess ;-) */
 }
+EXPORT_SYMBOL(wireless_send_event);
 
 /********************** ENHANCED IWSPY SUPPORT **********************/
 /*
@@ -1319,11 +1425,11 @@ void wireless_send_event(struct net_device *    dev,
  * Because this is called on the Rx path via wireless_spy_update(),
  * we want it to be efficient...
  */
-static inline struct iw_spy_data * get_spydata(struct net_device *dev)
+static inline struct iw_spy_data *get_spydata(struct net_device *dev)
 {
        /* This is the new way */
        if (dev->wireless_data)
-               return(dev->wireless_data->spy_data);
+               return dev->wireless_data->spy_data;
        return NULL;
 }
 
@@ -1377,6 +1483,7 @@ int iw_handler_set_spy(struct net_device *        dev,
 
        return 0;
 }
+EXPORT_SYMBOL(iw_handler_set_spy);
 
 /*------------------------------------------------------------------*/
 /*
@@ -1412,6 +1519,7 @@ int iw_handler_get_spy(struct net_device *        dev,
                spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
        return 0;
 }
+EXPORT_SYMBOL(iw_handler_get_spy);
 
 /*------------------------------------------------------------------*/
 /*
@@ -1438,6 +1546,7 @@ int iw_handler_set_thrspy(struct net_device *     dev,
 
        return 0;
 }
+EXPORT_SYMBOL(iw_handler_set_thrspy);
 
 /*------------------------------------------------------------------*/
 /*
@@ -1461,6 +1570,7 @@ int iw_handler_get_thrspy(struct net_device *     dev,
 
        return 0;
 }
+EXPORT_SYMBOL(iw_handler_get_thrspy);
 
 /*------------------------------------------------------------------*/
 /*
@@ -1537,10 +1647,4 @@ void wireless_spy_update(struct net_device *     dev,
                }
        }
 }
-
-EXPORT_SYMBOL(iw_handler_get_spy);
-EXPORT_SYMBOL(iw_handler_get_thrspy);
-EXPORT_SYMBOL(iw_handler_set_spy);
-EXPORT_SYMBOL(iw_handler_set_thrspy);
-EXPORT_SYMBOL(wireless_send_event);
 EXPORT_SYMBOL(wireless_spy_update);