root/net/wireless/wext-priv.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. iw_handler_get_private
  2. get_priv_size
  3. adjust_priv_size
  4. get_priv_descr_and_size
  5. ioctl_private_iw_point
  6. ioctl_private_call
  7. compat_private_call

   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 
  16 int 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 */
  46 static 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 
  57 static 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 
  65 static 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  */
  93 static 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 
 137 static 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 
 182 out:
 183         kfree(extra);
 184         return err;
 185 }
 186 
 187 int 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
 213 int 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

/* [<][>][^][v][top][bottom][index][help] */