/********************************************************************************************************* ** 函数名称: ioctl_private_iw_point ** 功能描述: ioctl priv ** 输 入 : ** 输 出 : OK or ERROR ** 全局变量: ** 调用模块: *********************************************************************************************************/ static int ioctl_private_iw_point (struct iw_point *iwp, unsigned int cmd, const struct iw_priv_args *descr, iw_handler handler, struct netif *dev, struct iw_request_info *info, int extra_size) { char *extra; int err; /* Check what user space is giving us */ if (IW_IS_SET(cmd)) { if (!iwp->pointer && iwp->length != 0) { return (-EFAULT); } if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK)) { return (-E2BIG); } } else if (!iwp->pointer) { return (-EFAULT); } extra = kzalloc(extra_size, GFP_KERNEL); if (!extra) { return -ENOMEM; } /* If it is a SET, get all the extra data in here */ if (IW_IS_SET(cmd) && (iwp->length != 0)) { if (copy_from_user(extra, iwp->pointer, extra_size)) { err = -EFAULT; goto out; } } /* Call the handler */ err = handler(dev, info, (union iwreq_data *) iwp, extra); /* If we have something to return to the user */ if (!err && 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, iwp); } if (copy_to_user(iwp->pointer, extra, extra_size)) { err = -EFAULT; } } out: kfree(extra); return (err); }
/********************************************************************************************************* ** 函数名称: get_priv_descr_and_size ** 功能描述: Wrapper to call a private Wireless Extension handler. ** 输 入 : ** 输 出 : OK or ERROR ** 全局变量: ** 调用模块: *********************************************************************************************************/ static int get_priv_descr_and_size (struct netif *dev, unsigned int cmd, const struct iw_priv_args **descrp) { const struct iw_priv_args *descr; int i, extra_size; struct iw_handler_def *handlers = (struct iw_handler_def *)dev->wireless_handlers; if (!handlers) { return (-EOPNOTSUPP); } descr = NULL; for (i = 0; i < handlers->num_private_args; i++) { if (cmd == handlers->private_args[i].cmd) { descr = &handlers->private_args[i]; break; } } extra_size = 0; if (descr) { if (IW_IS_SET(cmd)) { int offset = 0; /* For sub-ioctls */ /* Check for sub-ioctl handler */ if (descr->name[0] == '\0') { /* Reserve one int for sub-ioctl index */ offset = sizeof(u32); } /* Size of set arguments */ extra_size = get_priv_size(descr->set_args); /* Does it fits in iwr ? */ if ((descr->set_args & IW_PRIV_SIZE_FIXED) && ((extra_size + offset) <= IFNAMSIZ)) { extra_size = 0; } } else { /* Size of get arguments */ extra_size = get_priv_size(descr->get_args); /* Does it fits in iwr ? */ if ((descr->get_args & IW_PRIV_SIZE_FIXED) && (extra_size <= IFNAMSIZ)) { extra_size = 0; } } } *descrp = descr; return (extra_size); }
static io_return_t device_set_status(void *d, dev_flavor_t flavor, dev_status_t status, mach_msg_type_number_t count) { if (flavor == NET_FLAGS) { if (count != 1) return D_INVALID_SIZE; short flags = status[0]; struct net_data *net = (struct net_data *) d; dev_change_flags (net->dev, flags); /* Change the flags of the Mach device, too. */ net->ifnet.if_flags = net->dev->flags; return D_SUCCESS; } if(flavor < SIOCIWFIRST || flavor > SIOCIWLAST) return D_INVALID_OPERATION; if(! IW_IS_SET(flavor)) return D_INVALID_OPERATION; if(count * sizeof(int) < sizeof(struct ifreq)) return D_INVALID_OPERATION; struct net_data *nd = d; struct linux_device *dev = nd->dev; if(! dev->do_ioctl) return D_INVALID_OPERATION; if((flavor == SIOCSIWENCODE || flavor == SIOCSIWESSID || flavor == SIOCSIWNICKN || flavor == SIOCSIWSPY) && ((struct iwreq *) status)->u.data.pointer) { struct iw_point *iwp = &((struct iwreq *) status)->u.data; /* safety check whether the status array is long enough ... */ if(count * sizeof(int) < sizeof(struct ifreq) + iwp->length) return D_INVALID_OPERATION; /* make sure, iwp->pointer points to the correct address */ if(iwp->pointer) iwp->pointer = (void *) status + sizeof(struct ifreq); } int result = dev->do_ioctl(dev, (struct ifreq *) status, flavor); return result ? D_IO_ERROR : D_SUCCESS; }
/* entry point from dev ioctl */ int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, void __user *arg) { int ret; /* If command is `set a parameter', or * `get the encoding parameters', check if * the user has the right to do it */ if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT) && !capable(CAP_NET_ADMIN)) return -EPERM; dev_load(net, ifr->ifr_name); rtnl_lock(); ret = wireless_process_ioctl(net, ifr, cmd); rtnl_unlock(); if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct iwreq))) return -EFAULT; return ret; }
static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, const struct iw_ioctl_description *descr, iw_handler handler, struct net_device *dev, struct iw_request_info *info) { int err, extra_size, user_length = 0, essid_compat = 0; char *extra; /* Calculate space needed by arguments. Always allocate * for max space. */ extra_size = descr->max_tokens * descr->token_size; /* Check need for ESSID compatibility for WE < 21 */ switch (cmd) { case SIOCSIWESSID: case SIOCGIWESSID: case SIOCSIWNICKN: case SIOCGIWNICKN: if (iwp->length == descr->max_tokens + 1) essid_compat = 1; else if (IW_IS_SET(cmd) && (iwp->length != 0)) { char essid[IW_ESSID_MAX_SIZE + 1]; unsigned int len; len = iwp->length * descr->token_size; if (len > IW_ESSID_MAX_SIZE) return -EFAULT; err = copy_from_user(essid, iwp->pointer, len); if (err) return -EFAULT; if (essid[iwp->length - 1] == '\0') essid_compat = 1; } break; default: break; } iwp->length -= essid_compat; /* Check what user space is giving us */ if (IW_IS_SET(cmd)) { /* Check NULL pointer */ if (!iwp->pointer && iwp->length != 0) return -EFAULT; /* Check if number of token fits within bounds */ if (iwp->length > descr->max_tokens) return -E2BIG; if (iwp->length < descr->min_tokens) return -EINVAL; } else { /* Check NULL pointer */ if (!iwp->pointer) return -EFAULT; /* Save user space buffer size for checking */ user_length = iwp->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; /* Note : user_length is originally a __u16, * and token_size is controlled by us, * so extra_size won't get negative and * won't overflow... */ } } /* kzalloc() ensures NULL-termination for essid_compat. */ extra = kzalloc(extra_size, GFP_KERNEL); if (!extra) return -ENOMEM; /* If it is a SET, get all the extra data in here */ if (IW_IS_SET(cmd) && (iwp->length != 0)) { if (copy_from_user(extra, iwp->pointer, iwp->length * descr->token_size)) { err = -EFAULT; goto out; } if (cmd == SIOCSIWENCODEEXT) { struct iw_encode_ext *ee = (void *) extra; if (iwp->length < sizeof(*ee) + ee->key_len) return -EFAULT; } } if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) { /* * If this is a GET, but not NOMAX, it means that the extra * data is not bounded by userspace, but by max_tokens. Thus * set the length to max_tokens. This matches the extra data * allocation. * The driver should fill it with the number of tokens it * provided, and it may check iwp->length rather than having * knowledge of max_tokens. If the driver doesn't change the * iwp->length, this ioctl just copies back max_token tokens * filled with zeroes. Hopefully the driver isn't claiming * them to be valid data. */ iwp->length = descr->max_tokens; } err = handler(dev, info, (union iwreq_data *) iwp, extra); iwp->length += essid_compat; /* If we have something to return to the user */ if (!err && IW_IS_GET(cmd)) { /* Check if there is enough buffer up there */ if (user_length < iwp->length) { err = -E2BIG; goto out; } if (copy_to_user(iwp->pointer, extra, iwp->length * descr->token_size)) { err = -EFAULT; goto out; } } /* Generate an event to notify listeners of the change */ if ((descr->flags & IW_DESCR_FLAG_EVENT) && ((err == 0) || (err == -EIWCOMMIT))) { union iwreq_data *data = (union iwreq_data *) iwp; if (descr->flags & IW_DESCR_FLAG_RESTRICT) /* If the event is restricted, don't * export the payload. */ wireless_send_event(dev, cmd, data, NULL); else wireless_send_event(dev, cmd, data, extra); } out: kfree(extra); return err; }
/* * Wrapper to call a private Wireless Extension handler. * We do various checks and also take care of moving data between * user space and kernel space. * It's not as nice and slimline as the standard wrapper. The cause * is struct iw_priv_args, which was not really designed for the * job we are going here. * * IMPORTANT : This function prevent to set and get data on the same * IOCTL and enforce the SET/GET convention. Not doing it would be * far too hairy... * If you need to set and get data at the same time, please don't use * a iw_handler but process it in your ioctl handler (i.e. use the * old driver API). */ static inline int ioctl_private_call(struct net_device * dev, struct ifreq * ifr, unsigned int cmd, iw_handler handler) { struct iwreq * iwr = (struct iwreq *) ifr; struct iw_priv_args * descr = NULL; struct iw_request_info info; int extra_size = 0; int i; int ret = -EINVAL; /* Get the description of the IOCTL */ for(i = 0; i < dev->wireless_handlers->num_private_args; i++) if(cmd == dev->wireless_handlers->private_args[i].cmd) { descr = &(dev->wireless_handlers->private_args[i]); break; } #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n", ifr->ifr_name, cmd); if(descr) { printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n", dev->name, descr->name, descr->set_args, descr->get_args); } #endif /* WE_IOCTL_DEBUG */ /* Compute the size of the set/get arguments */ if(descr != NULL) { if(IW_IS_SET(cmd)) { int offset = 0; /* For sub-ioctls */ /* Check for sub-ioctl handler */ if(descr->name[0] == '\0') /* Reserve one int for sub-ioctl index */ offset = sizeof(__u32); /* Size of set arguments */ extra_size = get_priv_size(descr->set_args); /* Does it fits in iwr ? */ if((descr->set_args & IW_PRIV_SIZE_FIXED) && ((extra_size + offset) <= IFNAMSIZ)) extra_size = 0; } else { /* Size of set arguments */ extra_size = get_priv_size(descr->get_args); /* Does it fits in iwr ? */ if((descr->get_args & IW_PRIV_SIZE_FIXED) && (extra_size <= IFNAMSIZ)) extra_size = 0; } } /* Prepare the call */ info.cmd = cmd; info.flags = 0; /* Check if we have a pointer to user space data or not. */ if(extra_size == 0) { /* No extra arguments. Trivial to handle */ ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); } else { char * extra; int err; /* Check what user space is giving us */ if(IW_IS_SET(cmd)) { /* Check NULL pointer */ if((iwr->u.data.pointer == NULL) && (iwr->u.data.length != 0)) return -EFAULT; /* Does it fits within bounds ? */ if(iwr->u.data.length > (descr->set_args & IW_PRIV_SIZE_MASK)) return -E2BIG; } else { /* Check NULL pointer */ if(iwr->u.data.pointer == NULL) return -EFAULT; } #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", dev->name, extra_size); #endif /* WE_IOCTL_DEBUG */ /* Always allocate for max space. Easier, and won't last * long... */ extra = kmalloc(extra_size, GFP_KERNEL); if (extra == NULL) { return -ENOMEM; } /* If it is a SET, get all the extra data in here */ if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { err = copy_from_user(extra, iwr->u.data.pointer, extra_size); if (err) { kfree(extra); return -EFAULT; } #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s (WE) : Got %d elem\n", dev->name, iwr->u.data.length); #endif /* WE_IOCTL_DEBUG */ } /* Call the handler */ ret = handler(dev, &info, &(iwr->u), extra); /* If we have something to return to the user */ if (!ret && IW_IS_GET(cmd)) { err = copy_to_user(iwr->u.data.pointer, extra, extra_size); if (err) ret = -EFAULT; #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n", dev->name, iwr->u.data.length); #endif /* WE_IOCTL_DEBUG */ } /* Cleanup - I told you it wasn't that long ;-) */ kfree(extra); } /* Call commit handler if needed and defined */ if(ret == -EIWCOMMIT) ret = call_commit_handler(dev); return ret; }
/* * Wrapper to call a standard Wireless Extension handler. * We do various checks and also take care of moving data between * user space and kernel space. */ static inline int ioctl_standard_call(struct net_device * dev, struct ifreq * ifr, unsigned int cmd, iw_handler handler) { struct iwreq * iwr = (struct iwreq *) ifr; 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) return -EOPNOTSUPP; descr = &(standard_ioctl[cmd - SIOCIWFIRST]); #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n", ifr->ifr_name, cmd); printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); #endif /* WE_IOCTL_DEBUG */ /* Prepare the call */ info.cmd = cmd; info.flags = 0; /* Check if we have a pointer to user space data or not */ if(descr->header_type != IW_HEADER_TYPE_POINT) { /* No extra arguments. Trivial to handle */ ret = handler(dev, &info, &(iwr->u), NULL); #ifdef WE_SET_EVENT /* Generate an event to notify listeners of the change */ if((descr->flags & IW_DESCR_FLAG_EVENT) && ((ret == 0) || (ret == -EIWCOMMIT))) wireless_send_event(dev, cmd, &(iwr->u), NULL); #endif /* WE_SET_EVENT */ } else { char * extra; int err; /* Check what user space is giving us */ if(IW_IS_SET(cmd)) { /* Check NULL pointer */ if((iwr->u.data.pointer == NULL) && (iwr->u.data.length != 0)) return -EFAULT; /* Check if number of token fits within bounds */ if(iwr->u.data.length > descr->max_tokens) return -E2BIG; if(iwr->u.data.length < descr->min_tokens) return -EINVAL; } else { /* Check NULL pointer */ if(iwr->u.data.pointer == NULL) return -EFAULT; /* Save user space buffer size for checking */ user_size = iwr->u.data.length; } #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", dev->name, descr->max_tokens * descr->token_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); if (extra == NULL) { return -ENOMEM; } /* If it is a SET, get all the extra data in here */ if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { err = copy_from_user(extra, iwr->u.data.pointer, iwr->u.data.length * descr->token_size); if (err) { kfree(extra); return -EFAULT; } #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s (WE) : Got %d bytes\n", dev->name, iwr->u.data.length * descr->token_size); #endif /* WE_IOCTL_DEBUG */ } /* Call the handler */ ret = handler(dev, &info, &(iwr->u), extra); /* 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); kfree(extra); return -E2BIG; } #endif /* WE_STRICT_WRITE */ err = copy_to_user(iwr->u.data.pointer, extra, iwr->u.data.length * descr->token_size); if (err) ret = -EFAULT; #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n", dev->name, iwr->u.data.length * descr->token_size); #endif /* WE_IOCTL_DEBUG */ } #ifdef WE_SET_EVENT /* Generate an event to notify listeners of the change */ if((descr->flags & IW_DESCR_FLAG_EVENT) && ((ret == 0) || (ret == -EIWCOMMIT))) { if(descr->flags & IW_DESCR_FLAG_RESTRICT) /* If the event is restricted, don't * export the payload */ wireless_send_event(dev, cmd, &(iwr->u), NULL); else wireless_send_event(dev, cmd, &(iwr->u), extra); } #endif /* WE_SET_EVENT */ /* Cleanup - I told you it wasn't that long ;-) */ kfree(extra); } /* Call commit handler if needed and defined */ if(ret == -EIWCOMMIT) ret = call_commit_handler(dev); /* Here, we will generate the appropriate event if needed */ return ret; }
/* * Wrapper to call a standard Wireless Extension handler. * We do various checks and also take care of moving data between * user space and kernel space. */ static int ioctl_standard_call(struct net_device * dev, struct ifreq * ifr, unsigned int cmd, iw_handler handler) { struct iwreq * iwr = (struct iwreq *) ifr; const struct iw_ioctl_description * descr; struct iw_request_info info; int ret = -EINVAL; /* Get the description of the IOCTL */ if ((cmd - SIOCIWFIRST) >= standard_ioctl_num) return -EOPNOTSUPP; descr = &(standard_ioctl[cmd - SIOCIWFIRST]); /* Prepare the call */ info.cmd = cmd; info.flags = 0; /* Check if we have a pointer to user space data or not */ if (descr->header_type != IW_HEADER_TYPE_POINT) { /* No extra arguments. Trivial to handle */ ret = handler(dev, &info, &(iwr->u), NULL); /* Generate an event to notify listeners of the change */ if ((descr->flags & IW_DESCR_FLAG_EVENT) && ((ret == 0) || (ret == -EIWCOMMIT))) wireless_send_event(dev, cmd, &(iwr->u), NULL); } else { char * extra; int extra_size; int user_length = 0; int err; int essid_compat = 0; /* 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 need for ESSID compatibility for WE < 21 */ switch (cmd) { case SIOCSIWESSID: case SIOCGIWESSID: case SIOCSIWNICKN: case SIOCGIWNICKN: if (iwr->u.data.length == descr->max_tokens + 1) essid_compat = 1; else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { char essid[IW_ESSID_MAX_SIZE + 1]; err = copy_from_user(essid, iwr->u.data.pointer, iwr->u.data.length * descr->token_size); if (err) return -EFAULT; if (essid[iwr->u.data.length - 1] == '\0') essid_compat = 1; } break; default: break; } iwr->u.data.length -= essid_compat; /* Check what user space is giving us */ if (IW_IS_SET(cmd)) { /* Check NULL pointer */ if ((iwr->u.data.pointer == NULL) && (iwr->u.data.length != 0)) return -EFAULT; /* Check if number of token fits within bounds */ if (iwr->u.data.length > descr->max_tokens) return -E2BIG; if (iwr->u.data.length < descr->min_tokens) return -EINVAL; } else { /* Check NULL pointer */ if (iwr->u.data.pointer == NULL) return -EFAULT; /* Save user space buffer size for checking */ 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; /* Note : user_length is originally a __u16, * and token_size is controlled by us, * so extra_size won't get negative and * won't overflow... */ } } /* Create the kernel buffer */ /* kzalloc ensures NULL-termination for essid_compat */ extra = kzalloc(extra_size, GFP_KERNEL); if (extra == NULL) return -ENOMEM; /* If it is a SET, get all the extra data in here */ if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { err = copy_from_user(extra, iwr->u.data.pointer, iwr->u.data.length * descr->token_size); if (err) { kfree(extra); return -EFAULT; } } /* Call the handler */ ret = handler(dev, &info, &(iwr->u), extra); iwr->u.data.length += essid_compat; /* If we have something to return to the user */ if (!ret && IW_IS_GET(cmd)) { /* Check if there is enough buffer up there */ if (user_length < iwr->u.data.length) { kfree(extra); return -E2BIG; } err = copy_to_user(iwr->u.data.pointer, extra, iwr->u.data.length * descr->token_size); if (err) ret = -EFAULT; } /* Generate an event to notify listeners of the change */ if ((descr->flags & IW_DESCR_FLAG_EVENT) && ((ret == 0) || (ret == -EIWCOMMIT))) { if (descr->flags & IW_DESCR_FLAG_RESTRICT) /* If the event is restricted, don't * export the payload */ wireless_send_event(dev, cmd, &(iwr->u), NULL); else wireless_send_event(dev, cmd, &(iwr->u), extra); } /* Cleanup - I told you it wasn't that long ;-) */ kfree(extra); } /* Call commit handler if needed and defined */ if (ret == -EIWCOMMIT) ret = call_commit_handler(dev); /* Here, we will generate the appropriate event if needed */ return ret; }
/* ---------------------------------------------------------------- */ static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, const struct iw_ioctl_description *descr, iw_handler handler, struct net_device *dev, struct iw_request_info *info) { int err, extra_size, user_length = 0; char *extra; /* Calculate space needed by arguments. Always allocate * for max space. */ extra_size = descr->max_tokens * descr->token_size; /* Check what user space is giving us */ if (IW_IS_SET(cmd)) { /* Check NULL pointer */ if (!iwp->pointer && iwp->length != 0) return -EFAULT; /* Check if number of token fits within bounds */ if (iwp->length > descr->max_tokens) return -E2BIG; if (iwp->length < descr->min_tokens) return -EINVAL; } else { /* Check NULL pointer */ if (!iwp->pointer) return -EFAULT; /* Save user space buffer size for checking */ user_length = iwp->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; /* Note : user_length is originally a __u16, * and token_size is controlled by us, * so extra_size won't get negative and * won't overflow... */ } } extra = kzalloc(extra_size, GFP_KERNEL); if (!extra) return -ENOMEM; /* If it is a SET, get all the extra data in here */ if (IW_IS_SET(cmd) && (iwp->length != 0)) { if (copy_from_user(extra, iwp->pointer, iwp->length * descr->token_size)) { err = -EFAULT; goto out; } if (cmd == SIOCSIWENCODEEXT) { struct iw_encode_ext *ee = (void *) extra; if (iwp->length < sizeof(*ee) + ee->key_len) return -EFAULT; } } err = handler(dev, info, (union iwreq_data *) iwp, extra); /* If we have something to return to the user */ if (!err && IW_IS_GET(cmd)) { /* Check if there is enough buffer up there */ if (user_length < iwp->length) { err = -E2BIG; goto out; } if (copy_to_user(iwp->pointer, extra, iwp->length * descr->token_size)) { err = -EFAULT; goto out; } } /* Generate an event to notify listeners of the change */ if ((descr->flags & IW_DESCR_FLAG_EVENT) && err == -EIWCOMMIT) { union iwreq_data *data = (union iwreq_data *) iwp; if (descr->flags & IW_DESCR_FLAG_RESTRICT) /* If the event is restricted, don't * export the payload. */ wireless_send_event(dev, cmd, data, NULL); else wireless_send_event(dev, cmd, data, extra); } out: kfree(extra); return err; }
/* * Wrapper to call a standard Wireless Extension handler. * We do various checks and also take care of moving data between * user space and kernel space. */ static inline int ioctl_standard_call(struct net_device * dev, struct ifreq * ifr, unsigned int cmd, iw_handler handler) { struct iwreq * iwr = (struct iwreq *) ifr; const struct iw_ioctl_description * descr; struct iw_request_info info; int ret = -EINVAL; /* Get the description of the IOCTL */ descr = &(standard_ioctl[cmd - SIOCIWFIRST]); #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s : Found standard handler for 0x%04X\n", ifr->ifr_name, cmd); printk(KERN_DEBUG "Header type : %d, token type : %d, token_size : %d, max_token : %d\n", descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); #endif /* WE_IOCTL_DEBUG */ /* Prepare the call */ info.cmd = cmd; info.flags = 0; /* Check if we have a pointer to user space data or not */ if(descr->header_type != IW_HEADER_TYPE_POINT) { /* No extra arguments. Trivial to handle */ ret = handler(dev, &info, &(iwr->u), NULL); } else { char * extra; int err; /* Check what user space is giving us */ if(IW_IS_SET(cmd)) { /* Check NULL pointer */ if((iwr->u.data.pointer == NULL) && (iwr->u.data.length != 0)) return -EFAULT; /* Check if number of token fits within bounds */ if(iwr->u.data.length > descr->max_tokens) return -E2BIG; if(iwr->u.data.length < descr->min_tokens) return -EINVAL; } else { /* 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 < descr->max_tokens) return -E2BIG; #endif /* WE_STRICT_WRITE */ } #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "Malloc %d bytes\n", descr->max_tokens * descr->token_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); if (extra == NULL) { return -ENOMEM; } /* If it is a SET, get all the extra data in here */ if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { err = copy_from_user(extra, iwr->u.data.pointer, iwr->u.data.length * descr->token_size); if (err) { kfree(extra); return -EFAULT; } #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "Got %d bytes\n", iwr->u.data.length * descr->token_size); #endif /* WE_IOCTL_DEBUG */ } /* Call the handler */ ret = handler(dev, &info, &(iwr->u), extra); /* If we have something to return to the user */ if (!ret && IW_IS_GET(cmd)) { err = copy_to_user(iwr->u.data.pointer, extra, iwr->u.data.length * descr->token_size); if (err) ret = -EFAULT; #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "Wrote %d bytes\n", iwr->u.data.length * descr->token_size); #endif /* WE_IOCTL_DEBUG */ } /* Cleanup - I told you it wasn't that long ;-) */ kfree(extra); } /* Call commit handler if needed and defined */ if(ret == -EIWCOMMIT) ret = call_commit_handler(dev); /* Here, we will generate the appropriate event if needed */ return ret; }
int dev_ioctl(unsigned int cmd, void *arg) { switch(cmd) { case SIOCGIFCONF: (void) dev_ifconf((char *) arg); return 0; /* * Ioctl calls that can be done by all. */ case SIOCGIFFLAGS: case SIOCGIFADDR: case SIOCGIFDSTADDR: case SIOCGIFBRDADDR: case SIOCGIFNETMASK: case SIOCGIFMETRIC: case SIOCGIFMTU: case SIOCGIFMEM: case SIOCGIFHWADDR: case SIOCGIFSLAVE: case SIOCGIFMAP: return dev_ifsioc(arg, cmd); /* * Ioctl calls requiring the power of a superuser */ case SIOCSIFFLAGS: case SIOCSIFADDR: case SIOCSIFDSTADDR: case SIOCSIFBRDADDR: case SIOCSIFNETMASK: case SIOCSIFMETRIC: case SIOCSIFMTU: case SIOCSIFMEM: case SIOCSIFHWADDR: case SIOCSIFMAP: case SIOCSIFSLAVE: case SIOCADDMULTI: case SIOCDELMULTI: if (!suser()) return -EPERM; return dev_ifsioc(arg, cmd); case SIOCSIFLINK: return -EINVAL; /* * Unknown or private ioctl. */ default: if((cmd >= SIOCDEVPRIVATE) && (cmd <= (SIOCDEVPRIVATE + 15))) { return dev_ifsioc(arg, cmd); } #ifdef CONFIG_NET_RADIO if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) { if((IW_IS_SET(cmd)) && (!suser())) return -EPERM; return dev_ifsioc(arg, cmd); } #endif /* CONFIG_NET_RADIO */ return -EINVAL; } }