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