📄 wext.c
字号:
/* * Calculate size of private arguments */static inline int get_priv_size(__u16 args){ int num = args & IW_PRIV_SIZE_MASK; int type = (args & IW_PRIV_TYPE_MASK) >> 12; return num * iw_priv_type_size[type];}/* ---------------------------------------------------------------- *//* * Re-calculate the size of private arguments */static inline int adjust_priv_size(__u16 args, union iwreq_data * wrqu){ int num = wrqu->data.length; int max = args & IW_PRIV_SIZE_MASK; int type = (args & IW_PRIV_TYPE_MASK) >> 12; /* Make sure the driver doesn't goof up */ if (max < num) num = max; return num * iw_priv_type_size[type];}/* ---------------------------------------------------------------- *//* * Standard Wireless Handler : get wireless stats * Allow programatic access to /proc/net/wireless even if /proc * doesn't exist... Also more efficient... */static int iw_handler_get_iwstats(struct net_device * dev, struct iw_request_info * info, union iwreq_data * wrqu, char * extra){ /* Get stats from the driver */ struct iw_statistics *stats; stats = get_wireless_stats(dev); if (stats) { /* Copy statistics to extra */ memcpy(extra, stats, sizeof(struct iw_statistics)); wrqu->data.length = sizeof(struct iw_statistics); /* Check if we need to clear the updated flag */ if (wrqu->data.flags != 0) stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; return 0; } else return -EOPNOTSUPP;}/* ---------------------------------------------------------------- *//* * Standard Wireless Handler : get iwpriv definitions * Export the driver private handler definition * They will be picked up by tools like iwpriv... */static int iw_handler_get_private(struct net_device * dev, struct iw_request_info * info, union iwreq_data * wrqu, char * extra){ /* Check if the driver has something to export */ if ((dev->wireless_handlers->num_private_args == 0) || (dev->wireless_handlers->private_args == NULL)) return -EOPNOTSUPP; /* Check if there is enough buffer up there */ if (wrqu->data.length < dev->wireless_handlers->num_private_args) { /* User space can't know in advance how large the buffer * needs to be. Give it a hint, so that we can support * any size buffer we want somewhat efficiently... */ wrqu->data.length = dev->wireless_handlers->num_private_args; return -E2BIG; } /* Set the number of available ioctls. */ wrqu->data.length = dev->wireless_handlers->num_private_args; /* Copy structure to the user buffer. */ memcpy(extra, dev->wireless_handlers->private_args, sizeof(struct iw_priv_args) * wrqu->data.length); return 0;}/******************** /proc/net/wireless SUPPORT ********************//* * The /proc/net/wireless file is a human readable user-space interface * exporting various wireless specific statistics from the wireless devices. * This is the most popular part of the Wireless Extensions ;-) * * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). * The content of the file is basically the content of "struct iw_statistics". */#ifdef CONFIG_PROC_FS/* ---------------------------------------------------------------- *//* * Print one entry (line) of /proc/net/wireless */static void wireless_seq_printf_stats(struct seq_file *seq, struct net_device *dev){ /* Get stats from the driver */ struct iw_statistics *stats = get_wireless_stats(dev); if (stats) { seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " "%6d %6d %6d\n", dev->name, stats->status, stats->qual.qual, stats->qual.updated & IW_QUAL_QUAL_UPDATED ? '.' : ' ', ((__s32) stats->qual.level) - ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), stats->qual.updated & IW_QUAL_LEVEL_UPDATED ? '.' : ' ', ((__s32) stats->qual.noise) - ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), stats->qual.updated & IW_QUAL_NOISE_UPDATED ? '.' : ' ', stats->discard.nwid, stats->discard.code, stats->discard.fragment, stats->discard.retries, stats->discard.misc, stats->miss.beacon); stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; }}/* ---------------------------------------------------------------- *//* * Print info for /proc/net/wireless (print all entries) */static int wireless_seq_show(struct seq_file *seq, void *v){ if (v == SEQ_START_TOKEN) seq_printf(seq, "Inter-| sta-| Quality | Discarded " "packets | Missed | WE\n" " face | tus | link level noise | nwid " "crypt frag retry misc | beacon | %d\n", WIRELESS_EXT); else wireless_seq_printf_stats(seq, v); return 0;}static const struct seq_operations wireless_seq_ops = { .start = dev_seq_start, .next = dev_seq_next, .stop = dev_seq_stop, .show = wireless_seq_show,};static int wireless_seq_open(struct inode *inode, struct file *file){ struct seq_file *seq; int res; res = seq_open(file, &wireless_seq_ops); if (!res) { seq = file->private_data; seq->private = get_proc_net(inode); if (!seq->private) { seq_release(inode, file); res = -ENXIO; } } return res;}static int wireless_seq_release(struct inode *inode, struct file *file){ struct seq_file *seq = file->private_data; struct net *net = seq->private; put_net(net); return seq_release(inode, file);}static const struct file_operations wireless_seq_fops = { .owner = THIS_MODULE, .open = wireless_seq_open, .read = seq_read, .llseek = seq_lseek, .release = wireless_seq_release,};int wext_proc_init(struct net *net){ /* Create /proc/net/wireless entry */ if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops)) return -ENOMEM; return 0;}void wext_proc_exit(struct net *net){ proc_net_remove(net, "wireless");}#endif /* CONFIG_PROC_FS *//************************** IOCTL SUPPORT **************************//* * The original user space API to configure all those Wireless Extensions * is through IOCTLs. * In there, we check if we need to call the new driver API (iw_handler) * or just call the driver ioctl handler. *//* ---------------------------------------------------------------- *//* * 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;}/* ---------------------------------------------------------------- *//* * 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 int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, unsigned int cmd, iw_handler handler){ struct iwreq * iwr = (struct iwreq *) ifr; const 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; } /* 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 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; } } /* 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 if (iwr->u.data.pointer == NULL) return -EFAULT; /* 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; } } /* 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)) { /* 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, &(iwr->u)); } err = copy_to_user(iwr->u.data.pointer, extra, extra_size); if (err) ret = -EFAULT; } /* Cleanup - I told you it wasn't that long ;-) */ kfree(extra); } /* Call commit handler if needed and defined */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -