Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * This file implement the Wireless Extensions priv API.
0003  *
0004  * Authors :    Jean Tourrilhes - HPL - <jt@hpl.hp.com>
0005  * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
0006  * Copyright    2009 Johannes Berg <johannes@sipsolutions.net>
0007  *
0008  * (As all part of the Linux kernel, this file is GPL)
0009  */
0010 #include <linux/slab.h>
0011 #include <linux/wireless.h>
0012 #include <linux/netdevice.h>
0013 #include <net/iw_handler.h>
0014 #include <net/wext.h>
0015 
0016 int iw_handler_get_private(struct net_device *      dev,
0017                struct iw_request_info * info,
0018                union iwreq_data *       wrqu,
0019                char *           extra)
0020 {
0021     /* Check if the driver has something to export */
0022     if ((dev->wireless_handlers->num_private_args == 0) ||
0023        (dev->wireless_handlers->private_args == NULL))
0024         return -EOPNOTSUPP;
0025 
0026     /* Check if there is enough buffer up there */
0027     if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
0028         /* User space can't know in advance how large the buffer
0029          * needs to be. Give it a hint, so that we can support
0030          * any size buffer we want somewhat efficiently... */
0031         wrqu->data.length = dev->wireless_handlers->num_private_args;
0032         return -E2BIG;
0033     }
0034 
0035     /* Set the number of available ioctls. */
0036     wrqu->data.length = dev->wireless_handlers->num_private_args;
0037 
0038     /* Copy structure to the user buffer. */
0039     memcpy(extra, dev->wireless_handlers->private_args,
0040            sizeof(struct iw_priv_args) * wrqu->data.length);
0041 
0042     return 0;
0043 }
0044 
0045 /* Size (in bytes) of the various private data types */
0046 static const char iw_priv_type_size[] = {
0047     0,              /* IW_PRIV_TYPE_NONE */
0048     1,              /* IW_PRIV_TYPE_BYTE */
0049     1,              /* IW_PRIV_TYPE_CHAR */
0050     0,              /* Not defined */
0051     sizeof(__u32),          /* IW_PRIV_TYPE_INT */
0052     sizeof(struct iw_freq),     /* IW_PRIV_TYPE_FLOAT */
0053     sizeof(struct sockaddr),    /* IW_PRIV_TYPE_ADDR */
0054     0,              /* Not defined */
0055 };
0056 
0057 static int get_priv_size(__u16 args)
0058 {
0059     int num = args & IW_PRIV_SIZE_MASK;
0060     int type = (args & IW_PRIV_TYPE_MASK) >> 12;
0061 
0062     return num * iw_priv_type_size[type];
0063 }
0064 
0065 static int adjust_priv_size(__u16 args, struct iw_point *iwp)
0066 {
0067     int num = iwp->length;
0068     int max = args & IW_PRIV_SIZE_MASK;
0069     int type = (args & IW_PRIV_TYPE_MASK) >> 12;
0070 
0071     /* Make sure the driver doesn't goof up */
0072     if (max < num)
0073         num = max;
0074 
0075     return num * iw_priv_type_size[type];
0076 }
0077 
0078 /*
0079  * Wrapper to call a private Wireless Extension handler.
0080  * We do various checks and also take care of moving data between
0081  * user space and kernel space.
0082  * It's not as nice and slimline as the standard wrapper. The cause
0083  * is struct iw_priv_args, which was not really designed for the
0084  * job we are going here.
0085  *
0086  * IMPORTANT : This function prevent to set and get data on the same
0087  * IOCTL and enforce the SET/GET convention. Not doing it would be
0088  * far too hairy...
0089  * If you need to set and get data at the same time, please don't use
0090  * a iw_handler but process it in your ioctl handler (i.e. use the
0091  * old driver API).
0092  */
0093 static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd,
0094                    const struct iw_priv_args **descrp)
0095 {
0096     const struct iw_priv_args *descr;
0097     int i, extra_size;
0098 
0099     descr = NULL;
0100     for (i = 0; i < dev->wireless_handlers->num_private_args; i++) {
0101         if (cmd == dev->wireless_handlers->private_args[i].cmd) {
0102             descr = &dev->wireless_handlers->private_args[i];
0103             break;
0104         }
0105     }
0106 
0107     extra_size = 0;
0108     if (descr) {
0109         if (IW_IS_SET(cmd)) {
0110             int offset = 0; /* For sub-ioctls */
0111             /* Check for sub-ioctl handler */
0112             if (descr->name[0] == '\0')
0113                 /* Reserve one int for sub-ioctl index */
0114                 offset = sizeof(__u32);
0115 
0116             /* Size of set arguments */
0117             extra_size = get_priv_size(descr->set_args);
0118 
0119             /* Does it fits in iwr ? */
0120             if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
0121                ((extra_size + offset) <= IFNAMSIZ))
0122                 extra_size = 0;
0123         } else {
0124             /* Size of get arguments */
0125             extra_size = get_priv_size(descr->get_args);
0126 
0127             /* Does it fits in iwr ? */
0128             if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
0129                (extra_size <= IFNAMSIZ))
0130                 extra_size = 0;
0131         }
0132     }
0133     *descrp = descr;
0134     return extra_size;
0135 }
0136 
0137 static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd,
0138                   const struct iw_priv_args *descr,
0139                   iw_handler handler, struct net_device *dev,
0140                   struct iw_request_info *info, int extra_size)
0141 {
0142     char *extra;
0143     int err;
0144 
0145     /* Check what user space is giving us */
0146     if (IW_IS_SET(cmd)) {
0147         if (!iwp->pointer && iwp->length != 0)
0148             return -EFAULT;
0149 
0150         if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK))
0151             return -E2BIG;
0152     } else if (!iwp->pointer)
0153         return -EFAULT;
0154 
0155     extra = kzalloc(extra_size, GFP_KERNEL);
0156     if (!extra)
0157         return -ENOMEM;
0158 
0159     /* If it is a SET, get all the extra data in here */
0160     if (IW_IS_SET(cmd) && (iwp->length != 0)) {
0161         if (copy_from_user(extra, iwp->pointer, extra_size)) {
0162             err = -EFAULT;
0163             goto out;
0164         }
0165     }
0166 
0167     /* Call the handler */
0168     err = handler(dev, info, (union iwreq_data *) iwp, extra);
0169 
0170     /* If we have something to return to the user */
0171     if (!err && IW_IS_GET(cmd)) {
0172         /* Adjust for the actual length if it's variable,
0173          * avoid leaking kernel bits outside.
0174          */
0175         if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
0176             extra_size = adjust_priv_size(descr->get_args, iwp);
0177 
0178         if (copy_to_user(iwp->pointer, extra, extra_size))
0179             err =  -EFAULT;
0180     }
0181 
0182 out:
0183     kfree(extra);
0184     return err;
0185 }
0186 
0187 int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
0188                unsigned int cmd, struct iw_request_info *info,
0189                iw_handler handler)
0190 {
0191     int extra_size = 0, ret = -EINVAL;
0192     const struct iw_priv_args *descr;
0193 
0194     extra_size = get_priv_descr_and_size(dev, cmd, &descr);
0195 
0196     /* Check if we have a pointer to user space data or not. */
0197     if (extra_size == 0) {
0198         /* No extra arguments. Trivial to handle */
0199         ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
0200     } else {
0201         ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
0202                          handler, dev, info, extra_size);
0203     }
0204 
0205     /* Call commit handler if needed and defined */
0206     if (ret == -EIWCOMMIT)
0207         ret = call_commit_handler(dev);
0208 
0209     return ret;
0210 }
0211 
0212 #ifdef CONFIG_COMPAT
0213 int compat_private_call(struct net_device *dev, struct iwreq *iwr,
0214             unsigned int cmd, struct iw_request_info *info,
0215             iw_handler handler)
0216 {
0217     const struct iw_priv_args *descr;
0218     int ret, extra_size;
0219 
0220     extra_size = get_priv_descr_and_size(dev, cmd, &descr);
0221 
0222     /* Check if we have a pointer to user space data or not. */
0223     if (extra_size == 0) {
0224         /* No extra arguments. Trivial to handle */
0225         ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
0226     } else {
0227         struct compat_iw_point *iwp_compat;
0228         struct iw_point iwp;
0229 
0230         iwp_compat = (struct compat_iw_point *) &iwr->u.data;
0231         iwp.pointer = compat_ptr(iwp_compat->pointer);
0232         iwp.length = iwp_compat->length;
0233         iwp.flags = iwp_compat->flags;
0234 
0235         ret = ioctl_private_iw_point(&iwp, cmd, descr,
0236                          handler, dev, info, extra_size);
0237 
0238         iwp_compat->pointer = ptr_to_compat(iwp.pointer);
0239         iwp_compat->length = iwp.length;
0240         iwp_compat->flags = iwp.flags;
0241     }
0242 
0243     /* Call commit handler if needed and defined */
0244     if (ret == -EIWCOMMIT)
0245         ret = call_commit_handler(dev);
0246 
0247     return ret;
0248 }
0249 #endif