[PATCH 2.6] Wireless Extension v17 for Linus

From: Jean Tourrilhes (jt_at_bougret.hpl.hp.com)
Date: 08/04/04

  • Next message: Con Kolivas: "[PATCH][2/3] Sched batch for staircase"
    Date:	Tue, 3 Aug 2004 17:40:32 -0700
    To: Jeff Garzik <jgarzik@pobox.com>, netdev@oss.sgi.com, Linux kernel mailing list <linux-kernel@vger.kernel.org>
    
    

            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@gnu.org> :
     * - 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@hpl.hp.com>
    - * 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@hpl.hp.com>
    - * 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@hpl.hp.com>
    - * 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@gnu.org> :
    + * 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);
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/


  • Next message: Con Kolivas: "[PATCH][2/3] Sched batch for staircase"

    Relevant Pages