linux/net/wireless/wext-priv.c
<<
>>
Prefs
   1/*
   2 * This file implement the Wireless Extensions priv API.
   3 *
   4 * Authors :    Jean Tourrilhes - HPL - <jt@hpl.hp.com>
   5 * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
   6 * Copyright    2009 Johannes Berg <johannes@sipsolutions.net>
   7 *
   8 * (As all part of the Linux kernel, this file is GPL)
   9 */
  10#include <linux/slab.h>
  11#include <linux/wireless.h>
  12#include <linux/netdevice.h>
  13#include <net/iw_handler.h>
  14#include <net/wext.h>
  15
  16int iw_handler_get_private(struct net_device *          dev,
  17                           struct iw_request_info *     info,
  18                           union iwreq_data *           wrqu,
  19                           char *                       extra)
  20{
  21        /* Check if the driver has something to export */
  22        if ((dev->wireless_handlers->num_private_args == 0) ||
  23           (dev->wireless_handlers->private_args == NULL))
  24                return -EOPNOTSUPP;
  25
  26        /* Check if there is enough buffer up there */
  27        if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
  28                /* User space can't know in advance how large the buffer
  29                 * needs to be. Give it a hint, so that we can support
  30                 * any size buffer we want somewhat efficiently... */
  31                wrqu->data.length = dev->wireless_handlers->num_private_args;
  32                return -E2BIG;
  33        }
  34
  35        /* Set the number of available ioctls. */
  36        wrqu->data.length = dev->wireless_handlers->num_private_args;
  37
  38        /* Copy structure to the user buffer. */
  39        memcpy(extra, dev->wireless_handlers->private_args,
  40               sizeof(struct iw_priv_args) * wrqu->data.length);
  41
  42        return 0;
  43}
  44
  45/* Size (in bytes) of the various private data types */
  46static const char iw_priv_type_size[] = {
  47        0,                              /* IW_PRIV_TYPE_NONE */
  48        1,                              /* IW_PRIV_TYPE_BYTE */
  49        1,                              /* IW_PRIV_TYPE_CHAR */
  50        0,                              /* Not defined */
  51        sizeof(__u32),                  /* IW_PRIV_TYPE_INT */
  52        sizeof(struct iw_freq),         /* IW_PRIV_TYPE_FLOAT */
  53        sizeof(struct sockaddr),        /* IW_PRIV_TYPE_ADDR */
  54        0,                              /* Not defined */
  55};
  56
  57static int get_priv_size(__u16 args)
  58{
  59        int     num = args & IW_PRIV_SIZE_MASK;
  60        int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
  61
  62        return num * iw_priv_type_size[type];
  63}
  64
  65static int adjust_priv_size(__u16 args, struct iw_point *iwp)
  66{
  67        int     num = iwp->length;
  68        int     max = args & IW_PRIV_SIZE_MASK;
  69        int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
  70
  71        /* Make sure the driver doesn't goof up */
  72        if (max < num)
  73                num = max;
  74
  75        return num * iw_priv_type_size[type];
  76}
  77
  78/*
  79 * Wrapper to call a private Wireless Extension handler.
  80 * We do various checks and also take care of moving data between
  81 * user space and kernel space.
  82 * It's not as nice and slimline as the standard wrapper. The cause
  83 * is struct iw_priv_args, which was not really designed for the
  84 * job we are going here.
  85 *
  86 * IMPORTANT : This function prevent to set and get data on the same
  87 * IOCTL and enforce the SET/GET convention. Not doing it would be
  88 * far too hairy...
  89 * If you need to set and get data at the same time, please don't use
  90 * a iw_handler but process it in your ioctl handler (i.e. use the
  91 * old driver API).
  92 */
  93static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd,
  94                                   const struct iw_priv_args **descrp)
  95{
  96        const struct iw_priv_args *descr;
  97        int i, extra_size;
  98
  99        descr = NULL;
 100        for (i = 0; i < dev->wireless_handlers->num_private_args; i++) {
 101                if (cmd == dev->wireless_handlers->private_args[i].cmd) {
 102                        descr = &dev->wireless_handlers->private_args[i];
 103                        break;
 104                }
 105        }
 106
 107        extra_size = 0;
 108        if (descr) {
 109                if (IW_IS_SET(cmd)) {
 110                        int     offset = 0;     /* For sub-ioctls */
 111                        /* Check for sub-ioctl handler */
 112                        if (descr->name[0] == '\0')
 113                                /* Reserve one int for sub-ioctl index */
 114                                offset = sizeof(__u32);
 115
 116                        /* Size of set arguments */
 117                        extra_size = get_priv_size(descr->set_args);
 118
 119                        /* Does it fits in iwr ? */
 120                        if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
 121                           ((extra_size + offset) <= IFNAMSIZ))
 122                                extra_size = 0;
 123                } else {
 124                        /* Size of get arguments */
 125                        extra_size = get_priv_size(descr->get_args);
 126
 127                        /* Does it fits in iwr ? */
 128                        if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
 129                           (extra_size <= IFNAMSIZ))
 130                                extra_size = 0;
 131                }
 132        }
 133        *descrp = descr;
 134        return extra_size;
 135}
 136
 137static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd,
 138                                  const struct iw_priv_args *descr,
 139                                  iw_handler handler, struct net_device *dev,
 140                                  struct iw_request_info *info, int extra_size)
 141{
 142        char *extra;
 143        int err;
 144
 145        /* Check what user space is giving us */
 146        if (IW_IS_SET(cmd)) {
 147                if (!iwp->pointer && iwp->length != 0)
 148                        return -EFAULT;
 149
 150                if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK))
 151                        return -E2BIG;
 152        } else if (!iwp->pointer)
 153                return -EFAULT;
 154
 155        extra = kzalloc(extra_size, GFP_KERNEL);
 156        if (!extra)
 157                return -ENOMEM;
 158
 159        /* If it is a SET, get all the extra data in here */
 160        if (IW_IS_SET(cmd) && (iwp->length != 0)) {
 161                if (copy_from_user(extra, iwp->pointer, extra_size)) {
 162                        err = -EFAULT;
 163                        goto out;
 164                }
 165        }
 166
 167        /* Call the handler */
 168        err = handler(dev, info, (union iwreq_data *) iwp, extra);
 169
 170        /* If we have something to return to the user */
 171        if (!err && IW_IS_GET(cmd)) {
 172                /* Adjust for the actual length if it's variable,
 173                 * avoid leaking kernel bits outside.
 174                 */
 175                if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
 176                        extra_size = adjust_priv_size(descr->get_args, iwp);
 177
 178                if (copy_to_user(iwp->pointer, extra, extra_size))
 179                        err =  -EFAULT;
 180        }
 181
 182out:
 183        kfree(extra);
 184        return err;
 185}
 186
 187int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
 188                       unsigned int cmd, struct iw_request_info *info,
 189                       iw_handler handler)
 190{
 191        int extra_size = 0, ret = -EINVAL;
 192        const struct iw_priv_args *descr;
 193
 194        extra_size = get_priv_descr_and_size(dev, cmd, &descr);
 195
 196        /* Check if we have a pointer to user space data or not. */
 197        if (extra_size == 0) {
 198                /* No extra arguments. Trivial to handle */
 199                ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
 200        } else {
 201                ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
 202                                             handler, dev, info, extra_size);
 203        }
 204
 205        /* Call commit handler if needed and defined */
 206        if (ret == -EIWCOMMIT)
 207                ret = call_commit_handler(dev);
 208
 209        return ret;
 210}
 211
 212#ifdef CONFIG_COMPAT
 213int compat_private_call(struct net_device *dev, struct iwreq *iwr,
 214                        unsigned int cmd, struct iw_request_info *info,
 215                        iw_handler handler)
 216{
 217        const struct iw_priv_args *descr;
 218        int ret, extra_size;
 219
 220        extra_size = get_priv_descr_and_size(dev, cmd, &descr);
 221
 222        /* Check if we have a pointer to user space data or not. */
 223        if (extra_size == 0) {
 224                /* No extra arguments. Trivial to handle */
 225                ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
 226        } else {
 227                struct compat_iw_point *iwp_compat;
 228                struct iw_point iwp;
 229
 230                iwp_compat = (struct compat_iw_point *) &iwr->u.data;
 231                iwp.pointer = compat_ptr(iwp_compat->pointer);
 232                iwp.length = iwp_compat->length;
 233                iwp.flags = iwp_compat->flags;
 234
 235                ret = ioctl_private_iw_point(&iwp, cmd, descr,
 236                                             handler, dev, info, extra_size);
 237
 238                iwp_compat->pointer = ptr_to_compat(iwp.pointer);
 239                iwp_compat->length = iwp.length;
 240                iwp_compat->flags = iwp.flags;
 241        }
 242
 243        /* Call commit handler if needed and defined */
 244        if (ret == -EIWCOMMIT)
 245                ret = call_commit_handler(dev);
 246
 247        return ret;
 248}
 249#endif
 250