netdev
[Top] [All Lists]

[PATCH 2.6] Wireless Extension v17 for Linus

To: Jeff Garzik <jgarzik@xxxxxxxxx>, netdev@xxxxxxxxxxx, Linux kernel mailing list <linux-kernel@xxxxxxxxxxxxxxx>
Subject: [PATCH 2.6] Wireless Extension v17 for Linus
From: Jean Tourrilhes <jt@xxxxxxxxxxxxxxxxxx>
Date: Tue, 3 Aug 2004 17:40:32 -0700
Address: HP Labs, 1U-17, 1501 Page Mill road, Palo Alto, CA 94304, USA.
E-mail: jt@xxxxxxxxxx
Organisation: HP Labs Palo Alto
Reply-to: jt@xxxxxxxxxx
Sender: netdev-bounce@xxxxxxxxxxx
User-agent: Mutt/1.3.28i
        Hi Jeff,

        This is the patch to migrate Wireless Extension from WE-16 to
WE-17 for kernel 2.6.X. I would like you to queue that patch and
submit it to Linus as soon as 2.6.8 is released (so it can be fully
tested during 2.6.9). If you want, I can resend that as soon as 2.6.8
is released.

        The patch is basically unchanged compared to the version
posted to the netdev list and my web page one month ago, I just
re-diff to the latest kernel (2.6.8-rc2-bk12). The patch already
included feedback from various driver maintainers, and nobody else
complained, so I guess it's ready.
        The patch for some drivers inside the kernel will follow
(airo.c, wavelan.c, wavelan_cs). Patch for various other drivers
(orinoco, hostap, prism54) have been sent already to their maintainers
(one month ago) and basically waiting for this patch.

        Changelog :
 *      - Add flags to frequency -> auto/fixed
 *      - Document (struct iw_quality *)->updated, add new flags (INVALID)
 *      - Wireless Event capability in struct iw_range
 *      - Add support for relative TxPower (yick !)
 *      - Change the way we get to spy_data method for added safety and hostap
 *      - Remove spy #ifdef, they are always on -> cleaner code
 *      - Allow any size GET request if user specifies length > max
 *      - Start migrating get_wireless_stats to struct iw_handler_def
 * Based on patch from Pavel Roskin <proski@xxxxxxx> :
 *      - Fix kernel data leak to user space in private handler handling

        I also added on my page a version of Wireless Tools that use
RtNetlink instead of ioctls. This is not as clean as I would like, but
is fully functional (if you have WE-19). I know that you were
interested, so feel free to send feedback on that...

        Have fun...

        Jean

--------------------------------------------------------------------

diff -u -p linux/include/linux/netdevice.we16.h linux/include/linux/netdevice.h
--- linux/include/linux/netdevice.we16.h        Tue Aug  3 11:13:59 2004
+++ linux/include/linux/netdevice.h     Tue Aug  3 11:16:30 2004
@@ -304,7 +304,9 @@ struct net_device
 
        /* List of functions to handle Wireless Extensions (instead of ioctl).
         * See <net/iw_handler.h> for details. Jean II */
-       struct iw_handler_def * wireless_handlers;
+       const struct iw_handler_def *   wireless_handlers;
+       /* Instance data managed by the core of Wireless Extensions. */
+       struct iw_public_data * wireless_data;
 
        struct ethtool_ops *ethtool_ops;
 
diff -u -p linux/include/linux/wireless.we16.h linux/include/linux/wireless.h
--- linux/include/linux/wireless.we16.h Tue Aug  3 11:14:07 2004
+++ linux/include/linux/wireless.h      Tue Aug  3 11:23:15 2004
@@ -1,10 +1,10 @@
 /*
  * This file define a set of standard wireless extensions
  *
- * Version :   16      2.4.03
+ * Version :   17      21.6.04
  *
  * Authors :   Jean Tourrilhes - HPL - <jt@xxxxxxxxxx>
- * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 1997-2004 Jean Tourrilhes, All Rights Reserved.
  */
 
 #ifndef _LINUX_WIRELESS_H
@@ -47,12 +47,12 @@
  *     # include/net/iw_handler.h
  *
  * Note as well that /proc/net/wireless implementation has now moved in :
- *     # include/linux/wireless.c
+ *     # net/core/wireless.c
  *
  * Wireless Events (2002 -> onward) :
  * --------------------------------
  * Events are defined at the end of this file, and implemented in :
- *     # include/linux/wireless.c
+ *     # net/core/wireless.c
  *
  * Other comments :
  * --------------
@@ -82,7 +82,7 @@
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT   16
+#define WIRELESS_EXT   17
 
 /*
  * Changes :
@@ -175,6 +175,13 @@
  *     - Remove IW_MAX_GET_SPY because conflict with enhanced spy support
  *     - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy"
  *     - Add IW_ENCODE_TEMP and iw_range->encoding_login_index
+ *
+ * V16 to V17
+ * ----------
+ *     - Add flags to frequency -> auto/fixed
+ *     - Document (struct iw_quality *)->updated, add new flags (INVALID)
+ *     - Wireless Event capability in struct iw_range
+ *     - Add support for relative TxPower (yick !)
  */
 
 /**************************** CONSTANTS ****************************/
@@ -251,7 +258,7 @@
 
 /* -------------------- DEV PRIVATE IOCTL LIST -------------------- */
 
-/* These 16 ioctl are wireless device private.
+/* These 32 ioctl are wireless device private, for 16 commands.
  * Each driver is free to use them for whatever purpose it chooses,
  * however the driver *must* export the description of those ioctls
  * with SIOCGIWPRIV and *must* use arguments as defined below.
@@ -266,8 +273,8 @@
  * We now have 32 commands, so a bit more space ;-).
  * Also, all 'odd' commands are only usable by root and don't return the
  * content of ifr/iwr to user (but you are not obliged to use the set/get
- * convention, just use every other two command).
- * And I repeat : you are not obliged to use them with iwspy, but you
+ * convention, just use every other two command). More details in iwpriv.c.
+ * And I repeat : you are not forced to use them with iwpriv, but you
  * must be compliant with it.
  */
 
@@ -352,6 +359,18 @@
 #define IW_MODE_SECOND 5       /* Secondary master/repeater (backup) */
 #define IW_MODE_MONITOR        6       /* Passive monitor (listen only) */
 
+/* Statistics flags (bitmask in updated) */
+#define IW_QUAL_QUAL_UPDATED   0x1     /* Value was updated since last read */
+#define IW_QUAL_LEVEL_UPDATED  0x2
+#define IW_QUAL_NOISE_UPDATED  0x4
+#define IW_QUAL_QUAL_INVALID   0x10    /* Driver doesn't provide value */
+#define IW_QUAL_LEVEL_INVALID  0x20
+#define IW_QUAL_NOISE_INVALID  0x40
+
+/* Frequency flags */
+#define IW_FREQ_AUTO           0x00    /* Let the driver decides */
+#define IW_FREQ_FIXED          0x01    /* Force a specific value */
+
 /* Maximum number of size of encoding token available
  * they are listed in the range structure */
 #define IW_MAX_ENCODING_SIZES  8
@@ -390,6 +409,7 @@
 #define IW_TXPOW_TYPE          0x00FF  /* Type of value */
 #define IW_TXPOW_DBM           0x0000  /* Value is in dBm */
 #define IW_TXPOW_MWATT         0x0001  /* Value is in mW */
+#define IW_TXPOW_RELATIVE      0x0002  /* Value is in arbitrary units */
 #define IW_TXPOW_RANGE         0x1000  /* Range of value between min/max */
 
 /* Retry limits and lifetime flags available */
@@ -418,6 +438,25 @@
 /* Max number of char in custom event - use multiple of them if needed */
 #define IW_CUSTOM_MAX          256     /* In bytes */
 
+/* Event capability macros - in (struct iw_range *)->event_capa
+ * Because we have more than 32 possible events, we use an array of
+ * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */
+#define IW_EVENT_CAPA_BASE(cmd)                ((cmd >= SIOCIWFIRSTPRIV) ? \
+                                        (cmd - SIOCIWFIRSTPRIV + 0x60) : \
+                                        (cmd - SIOCSIWCOMMIT))
+#define IW_EVENT_CAPA_INDEX(cmd)       (IW_EVENT_CAPA_BASE(cmd) >> 5)
+#define IW_EVENT_CAPA_MASK(cmd)                (1 << (IW_EVENT_CAPA_BASE(cmd) 
& 0x1F))
+/* Event capability constants - event autogenerated by the kernel
+ * This list is valid for most 802.11 devices, customise as needed... */
+#define IW_EVENT_CAPA_K_0      (IW_EVENT_CAPA_MASK(0x8B04) | \
+                                IW_EVENT_CAPA_MASK(0x8B06) | \
+                                IW_EVENT_CAPA_MASK(0x8B1A))
+#define IW_EVENT_CAPA_K_1      (IW_EVENT_CAPA_MASK(0x8B2A))
+/* "Easy" macro to set events in iw_range (less efficient) */
+#define IW_EVENT_CAPA_SET(event_capa, cmd) 
(event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd))
+#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= 
IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; }
+
+
 /****************************** TYPES ******************************/
 
 /* --------------------------- SUBTYPES --------------------------- */
@@ -456,7 +495,7 @@ struct      iw_freq
        __s32           m;              /* Mantissa */
        __s16           e;              /* Exponent */
        __u8            i;              /* List index (when in range struct) */
-       __u8            pad;            /* Unused - just for alignement */
+       __u8            flags;          /* Flags (fixed/auto) */
 };
 
 /*
@@ -610,11 +649,12 @@ struct    iw_range
        /* Old Frequency (backward compat - moved lower ) */
        __u16           old_num_channels;
        __u8            old_num_frequency;
-       /* Filler to keep "version" at the same offset */
-       __s32           old_freq[6];
+
+       /* Wireless event capability bitmasks */
+       __u32           event_capa[6];
 
        /* signal level threshold range */
-       __s32   sensitivity;
+       __s32           sensitivity;
 
        /* Quality of link & SNR stuff */
        /* Quality range (link, level, noise)
diff -u -p linux/include/net/iw_handler.we16.h linux/include/net/iw_handler.h
--- linux/include/net/iw_handler.we16.h Tue Aug  3 11:14:22 2004
+++ linux/include/net/iw_handler.h      Tue Aug  3 11:18:46 2004
@@ -1,10 +1,10 @@
 /*
  * This file define the new driver API for Wireless Extensions
  *
- * Version :   5       4.12.02
+ * Version :   6       21.6.04
  *
  * Authors :   Jean Tourrilhes - HPL - <jt@xxxxxxxxxx>
- * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 2001-2004 Jean Tourrilhes, All Rights Reserved.
  */
 
 #ifndef _IW_HANDLER_H
@@ -206,7 +206,7 @@
  * will be needed...
  * I just plan to increment with each new version.
  */
-#define IW_HANDLER_VERSION     5
+#define IW_HANDLER_VERSION     6
 
 /*
  * Changes :
@@ -224,11 +224,18 @@
  * V4 to V5
  * --------
  *     - Add new spy support : struct iw_spy_data & prototypes
+ *
+ * V5 to V6
+ * --------
+ *     - Change the way we get to spy_data method for added safety
+ *     - Remove spy #ifdef, they are always on -> cleaner code
+ *     - Add IW_DESCR_FLAG_NOMAX flag for very large requests
+ *     - Start migrating get_wireless_stats to struct iw_handler_def
  */
 
 /**************************** CONSTANTS ****************************/
 
-/* Enable enhanced spy support. Disable to reduce footprint */
+/* Enhanced spy support available */
 #define IW_WIRELESS_SPY
 #define IW_WIRELESS_THRSPY
 
@@ -258,6 +265,7 @@
 #define IW_DESCR_FLAG_EVENT    0x0002  /* Generate an event on SET */
 #define IW_DESCR_FLAG_RESTRICT 0x0004  /* GET : request is ROOT only */
                                /* SET : Omit payload from generated iwevent */
+#define IW_DESCR_FLAG_NOMAX    0x0008  /* GET : no limit on request size */
 /* Driver level flags */
 #define IW_DESCR_FLAG_WAIT     0x0100  /* Wait for driver event */
 
@@ -303,31 +311,33 @@ struct iw_handler_def
 {
        /* Number of handlers defined (more precisely, index of the
         * last defined handler + 1) */
-       __u16                   num_standard;
-       __u16                   num_private;
+       const __u16             num_standard;
+       const __u16             num_private;
        /* Number of private arg description */
-       __u16                   num_private_args;
+       const __u16             num_private_args;
 
        /* Array of handlers for standard ioctls
         * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME]
         */
-       iw_handler *            standard;
+       const iw_handler *      standard;
 
        /* Array of handlers for private ioctls
         * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV]
         */
-       iw_handler *            private;
+       const iw_handler *      private;
 
        /* Arguments of private handler. This one is just a list, so you
         * can put it in any order you want and should not leave holes...
         * We will automatically export that to user space... */
-       struct iw_priv_args *   private_args;
+       const struct iw_priv_args *     private_args;
 
-       /* Driver enhanced spy support */
-       long                    spy_offset;     /* Spy data offset */
+       /* This field will be *removed* in the next version of WE */
+       const long              spy_offset;     /* DO NOT USE */
 
-       /* In the long term, get_wireless_stats will move from
-        * 'struct net_device' to here, to minimise bloat. */
+       /* New location of get_wireless_stats, to de-bloat struct net_device.
+        * The old pointer in struct net_device will be gradually phased
+        * out, and drivers are encouraged to use this one... */
+       struct iw_statistics*   (*get_wireless_stats)(struct net_device *dev);
 };
 
 /* ---------------------- IOCTL DESCRIPTION ---------------------- */
@@ -374,18 +384,29 @@ struct iw_ioctl_description
  */
 struct iw_spy_data
 {
-#ifdef IW_WIRELESS_SPY
        /* --- Standard spy support --- */
        int                     spy_number;
        u_char                  spy_address[IW_MAX_SPY][ETH_ALEN];
        struct iw_quality       spy_stat[IW_MAX_SPY];
-#ifdef IW_WIRELESS_THRSPY
        /* --- Enhanced spy support (event) */
        struct iw_quality       spy_thr_low;    /* Low threshold */
        struct iw_quality       spy_thr_high;   /* High threshold */
        u_char                  spy_thr_under[IW_MAX_SPY];
-#endif /* IW_WIRELESS_THRSPY */
-#endif /* IW_WIRELESS_SPY */
+};
+
+/* --------------------- DEVICE WIRELESS DATA --------------------- */
+/*
+ * This is all the wireless data specific to a device instance that
+ * is managed by the core of Wireless Extensions.
+ * We only keep pointer to those structures, so that a driver is free
+ * to share them between instances.
+ * This structure should be initialised before registering the device.
+ * Access to this data follow the same rules as any other struct net_device
+ * data (i.e. valid as long as struct net_device exist, same locking rules).
+ */
+struct iw_public_data {
+       /* Driver enhanced spy support */
+       struct iw_spy_data *    spy_data;
 };
 
 /**************************** PROTOTYPES ****************************/
@@ -393,6 +414,9 @@ struct iw_spy_data
  * Functions part of the Wireless Extensions (defined in net/core/wireless.c).
  * Those may be called only within the kernel.
  */
+
+/* Data needed by fs/compat_ioctl.c for 32->64 bit conversion */
+extern const char iw_priv_type_size[];
 
 /* First : function strictly used inside the kernel */
 
diff -u -p linux/net/core/dev.we16.c linux/net/core/dev.c
--- linux/net/core/dev.we16.c   Tue Aug  3 11:14:45 2004
+++ linux/net/core/dev.c        Tue Aug  3 11:15:09 2004
@@ -2787,7 +2787,7 @@ int dev_ioctl(unsigned int cmd, void __u
                                /* Follow me in net/core/wireless.c */
                                ret = wireless_process_ioctl(&ifr, cmd);
                                rtnl_unlock();
-                               if (!ret && IW_IS_GET(cmd) &&
+                               if (IW_IS_GET(cmd) &&
                                    copy_to_user(arg, &ifr,
                                                 sizeof(struct ifreq)))
                                        ret = -EFAULT;
diff -u -p linux/net/core/wireless.we16.c linux/net/core/wireless.c
--- linux/net/core/wireless.we16.c      Tue Aug  3 11:14:54 2004
+++ linux/net/core/wireless.c   Tue Aug  3 11:20:39 2004
@@ -2,7 +2,7 @@
  * This file implement the Wireless Extensions APIs.
  *
  * Authors :   Jean Tourrilhes - HPL - <jt@xxxxxxxxxx>
- * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 1997-2004 Jean Tourrilhes, All Rights Reserved.
  *
  * (As all part of the Linux kernel, this file is GPL)
  */
@@ -48,6 +48,15 @@
  *     o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
  *     o Add enhanced spy support : iw_handler_set_thrspy() and event.
  *     o Add WIRELESS_EXT version display in /proc/net/wireless
+ *
+ * v6 - 18.06.04 - Jean II
+ *     o Change get_spydata() method for added safety
+ *     o Remove spy #ifdef, they are always on -> cleaner code
+ *     o Allow any size GET request is user specifies length > max
+ *     o Start migrating get_wireless_stats to struct iw_handler_def
+ *     o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
+ * Based on patch from Pavel Roskin <proski@xxxxxxx> :
+ *     o Fix kernel data leak to user space in private handler handling
  */
 
 /***************************** INCLUDES *****************************/
@@ -69,10 +78,6 @@
 
 /**************************** CONSTANTS ****************************/
 
-/* Enough lenience, let's make sure things are proper... */
-#define WE_STRICT_WRITE                /* Check write buffer size */
-/* I'll probably drop both the define and kernel message in the next version */
-
 /* Debugging stuff */
 #undef WE_IOCTL_DEBUG          /* Debug IOCTL API */
 #undef WE_EVENT_DEBUG          /* Debug Event dispatcher */
@@ -186,6 +191,7 @@ static const struct iw_ioctl_description
                .token_size     = sizeof(struct sockaddr) +
                                  sizeof(struct iw_quality),
                .max_tokens     = IW_MAX_AP,
+               .flags          = IW_DESCR_FLAG_NOMAX,
        },
        [SIOCSIWSCAN    - SIOCIWFIRST] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
@@ -194,6 +200,7 @@ static const struct iw_ioctl_description
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_SCAN_MAX_DATA,
+               .flags          = IW_DESCR_FLAG_NOMAX,
        },
        [SIOCSIWESSID   - SIOCIWFIRST] = {
                .header_type    = IW_HEADER_TYPE_POINT,
@@ -296,7 +303,7 @@ static const int standard_event_num = (s
                                       sizeof(struct iw_ioctl_description));
 
 /* Size (in bytes) of the various private data types */
-static const char priv_type_size[] = {
+const char iw_priv_type_size[] = {
        0,                              /* IW_PRIV_TYPE_NONE */
        1,                              /* IW_PRIV_TYPE_BYTE */
        1,                              /* IW_PRIV_TYPE_CHAR */
@@ -363,12 +370,15 @@ static inline iw_handler get_handler(str
  */
 static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
 {
+       /* New location */
+       if((dev->wireless_handlers != NULL) &&
+          (dev->wireless_handlers->get_wireless_stats != NULL))
+               return dev->wireless_handlers->get_wireless_stats(dev);
+
+       /* Old location, will be phased out in next WE */
        return (dev->get_wireless_stats ?
                dev->get_wireless_stats(dev) :
                (struct iw_statistics *) NULL);
-       /* In the future, get_wireless_stats may move from 'struct net_device'
-        * to 'struct iw_handler_def', to de-bloat struct net_device.
-        * Definitely worse a thought... */
 }
 
 /* ---------------------------------------------------------------- */
@@ -403,14 +413,32 @@ static inline int call_commit_handler(st
 
 /* ---------------------------------------------------------------- */
 /*
- * Number of private arguments
+ * Calculate size of private arguments
  */
 static inline int get_priv_size(__u16  args)
 {
        int     num = args & IW_PRIV_SIZE_MASK;
        int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
 
-       return num * priv_type_size[type];
+       return num * iw_priv_type_size[type];
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Re-calculate the size of private arguments
+ */
+static inline int adjust_priv_size(__u16               args,
+                                  union iwreq_data *   wrqu)
+{
+       int     num = wrqu->data.length;
+       int     max = args & IW_PRIV_SIZE_MASK;
+       int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+       /* Make sure the driver doesn't goof up */
+       if (max < num)
+               num = max;
+
+       return num * iw_priv_type_size[type];
 }
 
 
@@ -440,11 +468,14 @@ static __inline__ void wireless_seq_prin
                seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
                                "%6d %6d   %6d\n",
                           dev->name, stats->status, stats->qual.qual,
-                          stats->qual.updated & 1 ? '.' : ' ',
+                          stats->qual.updated & IW_QUAL_QUAL_UPDATED
+                          ? '.' : ' ',
                           ((__u8) stats->qual.level),
-                          stats->qual.updated & 2 ? '.' : ' ',
+                          stats->qual.updated & IW_QUAL_LEVEL_UPDATED
+                          ? '.' : ' ',
                           ((__u8) stats->qual.noise),
-                          stats->qual.updated & 4 ? '.' : ' ',
+                          stats->qual.updated & IW_QUAL_NOISE_UPDATED
+                          ? '.' : ' ',
                           stats->discard.nwid, stats->discard.code,
                           stats->discard.fragment, stats->discard.retries,
                           stats->discard.misc, stats->miss.beacon);
@@ -555,13 +586,15 @@ static inline int ioctl_export_private(s
        /* Check NULL pointer */
        if(iwr->u.data.pointer == NULL)
                return -EFAULT;
-#ifdef WE_STRICT_WRITE
+
        /* Check if there is enough buffer up there */
        if(iwr->u.data.length < dev->wireless_handlers->num_private_args) {
-               printk(KERN_ERR "%s (WE) : Buffer for request SIOCGIWPRIV too 
small (%d<%d)\n", dev->name, iwr->u.data.length, 
dev->wireless_handlers->num_private_args);
+               /* User space can't know in advance how large the buffer
+                * needs to be. Give it a hint, so that we can support
+                * any size buffer we want somewhat efficiently... */
+               iwr->u.data.length = dev->wireless_handlers->num_private_args;
                return -E2BIG;
        }
-#endif /* WE_STRICT_WRITE */
 
        /* Set the number of available ioctls. */
        iwr->u.data.length = dev->wireless_handlers->num_private_args;
@@ -590,7 +623,6 @@ static inline int ioctl_standard_call(st
        const struct iw_ioctl_description *     descr;
        struct iw_request_info                  info;
        int                                     ret = -EINVAL;
-       int                                     user_size = 0;
 
        /* Get the description of the IOCTL */
        if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
@@ -621,8 +653,14 @@ static inline int ioctl_standard_call(st
 #endif /* WE_SET_EVENT */
        } else {
                char *  extra;
+               int     extra_size;
+               int     user_length = 0;
                int     err;
 
+               /* 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 what user space is giving us */
                if(IW_IS_SET(cmd)) {
                        /* Check NULL pointer */
@@ -639,18 +677,29 @@ static inline int ioctl_standard_call(st
                        if(iwr->u.data.pointer == NULL)
                                return -EFAULT;
                        /* Save user space buffer size for checking */
-                       user_size = iwr->u.data.length;
+                       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;
+                       }
                }
 
 #ifdef WE_IOCTL_DEBUG
                printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
-                      dev->name, descr->max_tokens * descr->token_size);
+                      dev->name, extra_size);
 #endif /* WE_IOCTL_DEBUG */
 
-               /* Always allocate for max space. Easier, and won't last
-                * long... */
-               extra = kmalloc(descr->max_tokens * descr->token_size,
-                               GFP_KERNEL);
+               /* Create the kernel buffer */
+               extra = kmalloc(extra_size, GFP_KERNEL);
                if (extra == NULL) {
                        return -ENOMEM;
                }
@@ -676,14 +725,11 @@ static inline int ioctl_standard_call(st
 
                /* If we have something to return to the user */
                if (!ret && IW_IS_GET(cmd)) {
-#ifdef WE_STRICT_WRITE
                        /* Check if there is enough buffer up there */
-                       if(user_size < iwr->u.data.length) {
-                               printk(KERN_ERR "%s (WE) : Buffer for request 
%04X too small (%d<%d)\n", dev->name, cmd, user_size, iwr->u.data.length);
+                       if(user_length < iwr->u.data.length) {
                                kfree(extra);
                                return -E2BIG;
                        }
-#endif /* WE_STRICT_WRITE */
 
                        err = copy_to_user(iwr->u.data.pointer, extra,
                                           iwr->u.data.length *
@@ -746,7 +792,7 @@ static inline int ioctl_private_call(str
                                     iw_handler         handler)
 {
        struct iwreq *                  iwr = (struct iwreq *) ifr;
-       struct iw_priv_args *           descr = NULL;
+       const struct iw_priv_args *     descr = NULL;
        struct iw_request_info          info;
        int                             extra_size = 0;
        int                             i;
@@ -786,7 +832,7 @@ static inline int ioctl_private_call(str
                           ((extra_size + offset) <= IFNAMSIZ))
                                extra_size = 0;
                } else {
-                       /* Size of set arguments */
+                       /* Size of get arguments */
                        extra_size = get_priv_size(descr->get_args);
 
                        /* Does it fits in iwr ? */
@@ -816,7 +862,7 @@ static inline int ioctl_private_call(str
                                return -EFAULT;
 
                        /* Does it fits within bounds ? */
-                       if(iwr->u.data.length > (descr->set_args &
+                       if(iwr->u.data.length > (descr->get_args &
                                                 IW_PRIV_SIZE_MASK))
                                return -E2BIG;
                } else {
@@ -856,6 +902,14 @@ static inline int ioctl_private_call(str
 
                /* If we have something to return to the user */
                if (!ret && 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,
+                                                             &(iwr->u));
+                       }
+
                        err = copy_to_user(iwr->u.data.pointer, extra,
                                           extra_size);
                        if (err)
@@ -1127,9 +1181,25 @@ void wireless_send_event(struct net_devi
  * One of the main advantage of centralising spy support here is that
  * it becomes much easier to improve and extend it without having to touch
  * the drivers. One example is the addition of the Spy-Threshold events.
- * Note : IW_WIRELESS_SPY is defined in iw_handler.h
  */
 
+/* ---------------------------------------------------------------- */
+/*
+ * Return the pointer to the spy data in the driver.
+ * 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)
+{
+       /* This is the new way */
+       if(dev->wireless_data)
+               return(dev->wireless_data->spy_data);
+
+       /* This is the old way. Doesn't work for multi-headed drivers.
+        * It will be removed in the next version of WE. */
+       return (dev->priv + dev->wireless_handlers->spy_offset);
+}
+
 /*------------------------------------------------------------------*/
 /*
  * Standard Wireless Handler : set Spy List
@@ -1139,16 +1209,30 @@ int iw_handler_set_spy(struct net_device
                       union iwreq_data *       wrqu,
                       char *                   extra)
 {
-#ifdef IW_WIRELESS_SPY
-       struct iw_spy_data *    spydata = (dev->priv +
-                                          dev->wireless_handlers->spy_offset);
+       struct iw_spy_data *    spydata = get_spydata(dev);
        struct sockaddr *       address = (struct sockaddr *) extra;
 
+       if(!dev->wireless_data)
+               /* Help user know that driver needs updating */
+               printk(KERN_DEBUG "%s (WE) : Driver using old/buggy spy 
support, please fix driver !\n",
+                      dev->name);
+       /* Make sure driver is not buggy or using the old API */
+       if(!spydata)
+               return -EOPNOTSUPP;
+
        /* Disable spy collection while we copy the addresses.
-        * As we don't disable interrupts, we need to do this to avoid races.
-        * As we are the only writer, this is good enough. */
+        * While we copy addresses, any call to wireless_spy_update()
+        * will NOP. This is OK, as anyway the addresses are changing. */
        spydata->spy_number = 0;
 
+       /* We want to operate without locking, because wireless_spy_update()
+        * most likely will happen in the interrupt handler, and therefore
+        * have it own locking constraints and needs performance.
+        * The rtnl_lock() make sure we don't race with the other iw_handlers.
+        * This make sure wireless_spy_update() "see" that the spy list
+        * is temporarily disabled. */
+       wmb();
+
        /* Are there are addresses to copy? */
        if(wrqu->data.length > 0) {
                int i;
@@ -1174,13 +1258,14 @@ int iw_handler_set_spy(struct net_device
                               spydata->spy_address[i][5]);
 #endif /* WE_SPY_DEBUG */
        }
+
+       /* Make sure above is updated before re-enabling */
+       wmb();
+
        /* Enable addresses */
        spydata->spy_number = wrqu->data.length;
 
        return 0;
-#else /* IW_WIRELESS_SPY */
-       return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_SPY */
 }
 
 /*------------------------------------------------------------------*/
@@ -1192,12 +1277,14 @@ int iw_handler_get_spy(struct net_device
                       union iwreq_data *       wrqu,
                       char *                   extra)
 {
-#ifdef IW_WIRELESS_SPY
-       struct iw_spy_data *    spydata = (dev->priv +
-                                          dev->wireless_handlers->spy_offset);
+       struct iw_spy_data *    spydata = get_spydata(dev);
        struct sockaddr *       address = (struct sockaddr *) extra;
        int                     i;
 
+       /* Make sure driver is not buggy or using the old API */
+       if(!spydata)
+               return -EOPNOTSUPP;
+
        wrqu->data.length = spydata->spy_number;
 
        /* Copy addresses. */
@@ -1214,9 +1301,6 @@ int iw_handler_get_spy(struct net_device
        for(i = 0; i < spydata->spy_number; i++)
                spydata->spy_stat[i].updated = 0;
        return 0;
-#else /* IW_WIRELESS_SPY */
-       return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_SPY */
 }
 
 /*------------------------------------------------------------------*/
@@ -1228,11 +1312,13 @@ int iw_handler_set_thrspy(struct net_dev
                          union iwreq_data *    wrqu,
                          char *                extra)
 {
-#ifdef IW_WIRELESS_THRSPY
-       struct iw_spy_data *    spydata = (dev->priv +
-                                          dev->wireless_handlers->spy_offset);
+       struct iw_spy_data *    spydata = get_spydata(dev);
        struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
 
+       /* Make sure driver is not buggy or using the old API */
+       if(!spydata)
+               return -EOPNOTSUPP;
+
        /* Just do it */
        memcpy(&(spydata->spy_thr_low), &(threshold->low),
               2 * sizeof(struct iw_quality));
@@ -1245,9 +1331,6 @@ int iw_handler_set_thrspy(struct net_dev
 #endif /* WE_SPY_DEBUG */
 
        return 0;
-#else /* IW_WIRELESS_THRSPY */
-       return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_THRSPY */
 }
 
 /*------------------------------------------------------------------*/
@@ -1259,22 +1342,20 @@ int iw_handler_get_thrspy(struct net_dev
                          union iwreq_data *    wrqu,
                          char *                extra)
 {
-#ifdef IW_WIRELESS_THRSPY
-       struct iw_spy_data *    spydata = (dev->priv +
-                                          dev->wireless_handlers->spy_offset);
+       struct iw_spy_data *    spydata = get_spydata(dev);
        struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
 
+       /* Make sure driver is not buggy or using the old API */
+       if(!spydata)
+               return -EOPNOTSUPP;
+
        /* Just do it */
        memcpy(&(threshold->low), &(spydata->spy_thr_low),
               2 * sizeof(struct iw_quality));
 
        return 0;
-#else /* IW_WIRELESS_THRSPY */
-       return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_THRSPY */
 }
 
-#ifdef IW_WIRELESS_THRSPY
 /*------------------------------------------------------------------*/
 /*
  * Prepare and send a Spy Threshold event
@@ -1312,7 +1393,6 @@ static void iw_send_thrspy_event(struct 
        /* Send event to user space */
        wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
 }
-#endif /* IW_WIRELESS_THRSPY */
 
 /* ---------------------------------------------------------------- */
 /*
@@ -1325,12 +1405,14 @@ void wireless_spy_update(struct net_devi
                         unsigned char *        address,
                         struct iw_quality *    wstats)
 {
-#ifdef IW_WIRELESS_SPY
-       struct iw_spy_data *    spydata = (dev->priv +
-                                          dev->wireless_handlers->spy_offset);
+       struct iw_spy_data *    spydata = get_spydata(dev);
        int                     i;
        int                     match = -1;
 
+       /* Make sure driver is not buggy or using the old API */
+       if(!spydata)
+               return;
+
 #ifdef WE_SPY_DEBUG
        printk(KERN_DEBUG "wireless_spy_update() :  offset %ld, spydata %p, 
address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_handlers->spy_offset, 
spydata, address[0], address[1], address[2], address[3], address[4], 
address[5]);
 #endif /* WE_SPY_DEBUG */
@@ -1342,7 +1424,7 @@ void wireless_spy_update(struct net_devi
                               sizeof(struct iw_quality));
                        match = i;
                }
-#ifdef IW_WIRELESS_THRSPY
+
        /* Generate an event if we cross the spy threshold.
         * To avoid event storms, we have a simple hysteresis : we generate
         * event only when we go under the low threshold or above the
@@ -1362,8 +1444,6 @@ void wireless_spy_update(struct net_devi
                        }
                }
        }
-#endif /* IW_WIRELESS_THRSPY */
-#endif /* IW_WIRELESS_SPY */
 }
 
 EXPORT_SYMBOL(iw_handler_get_spy);

<Prev in Thread] Current Thread [Next in Thread>