📄 wext.c
字号:
/* * This file implement the Wireless Extensions APIs. * * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. * * (As all part of the Linux kernel, this file is GPL) *//************************** DOCUMENTATION **************************//* * API definition : * -------------- * See <linux/wireless.h> for details of the APIs and the rest. * * History : * ------- * * v1 - 5.12.01 - Jean II * o Created this file. * * v2 - 13.12.01 - Jean II * o Move /proc/net/wireless stuff from net/core/dev.c to here * o Make Wireless Extension IOCTLs go through here * o Added iw_handler handling ;-) * o Added standard ioctl description * o Initial dumb commit strategy based on orinoco.c * * v3 - 19.12.01 - Jean II * o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call * o Add event dispatcher function * o Add event description * o Propagate events as rtnetlink IFLA_WIRELESS option * o Generate event on selected SET requests * * v4 - 18.04.02 - Jean II * o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1 * * v5 - 21.06.02 - Jean II * o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup) * o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes * o Add IWEVCUSTOM for driver specific event/scanning token * o Turn on WE_STRICT_WRITE by default + kernel warning * o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num) * o Fix off-by-one in test (extra_size <= IFNAMSIZ) * * v6 - 9.01.03 - Jean II * o Add common spy support : iw_handler_set_spy(), wireless_spy_update() * o Add enhanced spy support : iw_handler_set_thrspy() and event. * o Add WIRELESS_EXT version display in /proc/net/wireless * * v6 - 18.06.04 - Jean II * o Change get_spydata() method for added safety * o Remove spy #ifdef, they are always on -> cleaner code * o Allow any size GET request if user specifies length > max * and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV * o Start migrating get_wireless_stats to struct iw_handler_def * o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus * Based on patch from Pavel Roskin <proski@gnu.org> : * o Fix kernel data leak to user space in private handler handling * * v7 - 18.3.05 - Jean II * o Remove (struct iw_point *)->pointer from events and streams * o Remove spy_offset from struct iw_handler_def * o Start deprecating dev->get_wireless_stats, output a warning * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats) * * v8 - 17.02.06 - Jean II * o RtNetlink requests support (SET/GET) * * v8b - 03.08.06 - Herbert Xu * o Fix Wireless Event locking issues. * * v9 - 14.3.06 - Jean II * o Change length in ESSID and NICK to strlen() instead of strlen()+1 * o Make standard_ioctl_num and standard_event_num unsigned * o Remove (struct net_device *)->get_wireless_stats() * * v10 - 16.3.07 - Jean II * o Prevent leaking of kernel space in stream on 64 bits. *//***************************** INCLUDES *****************************/#include <linux/module.h>#include <linux/types.h> /* off_t */#include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */#include <linux/proc_fs.h>#include <linux/rtnetlink.h> /* rtnetlink stuff */#include <linux/seq_file.h>#include <linux/init.h> /* for __init */#include <linux/if_arp.h> /* ARPHRD_ETHER */#include <linux/etherdevice.h> /* compare_ether_addr */#include <linux/interrupt.h>#include <net/net_namespace.h>#include <linux/wireless.h> /* Pretty obvious */#include <net/iw_handler.h> /* New driver API */#include <net/netlink.h>#include <net/wext.h>#include <asm/uaccess.h> /* copy_to_user() *//************************* GLOBAL VARIABLES *************************//* * You should not use global variables, because of re-entrancy. * On our case, it's only const, so it's OK... *//* * Meta-data about all the standard Wireless Extension request we * know about. */static const struct iw_ioctl_description standard_ioctl[] = { [SIOCSIWCOMMIT - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_NULL, }, [SIOCGIWNAME - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_CHAR, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWNWID - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, .flags = IW_DESCR_FLAG_EVENT, }, [SIOCGIWNWID - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWFREQ - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_FREQ, .flags = IW_DESCR_FLAG_EVENT, }, [SIOCGIWFREQ - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_FREQ, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWMODE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_UINT, .flags = IW_DESCR_FLAG_EVENT, }, [SIOCGIWMODE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_UINT, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWSENS - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWSENS - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWRANGE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_NULL, }, [SIOCGIWRANGE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_range), .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWPRIV - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_NULL, }, [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_priv_args), .max_tokens = 16, .flags = IW_DESCR_FLAG_NOMAX, }, [SIOCSIWSTATS - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_NULL, }, [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_statistics), .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWSPY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr), .max_tokens = IW_MAX_SPY, }, [SIOCGIWSPY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_SPY, }, [SIOCSIWTHRSPY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_thrspy), .min_tokens = 1, .max_tokens = 1, }, [SIOCGIWTHRSPY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_thrspy), .min_tokens = 1, .max_tokens = 1, }, [SIOCSIWAP - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_ADDR, }, [SIOCGIWAP - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_ADDR, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWMLME - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_mlme), .max_tokens = sizeof(struct iw_mlme), }, [SIOCGIWAPLIST - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_AP, .flags = IW_DESCR_FLAG_NOMAX, }, [SIOCSIWSCAN - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = 0, .max_tokens = sizeof(struct iw_scan_req), }, [SIOCGIWSCAN - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_SCAN_MAX_DATA, .flags = IW_DESCR_FLAG_NOMAX, }, [SIOCSIWESSID - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, .flags = IW_DESCR_FLAG_EVENT, }, [SIOCGIWESSID - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWNICKN - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, }, [SIOCGIWNICKN - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, }, [SIOCSIWRATE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWRATE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWRTS - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWRTS - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWFRAG - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWFRAG - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWTXPOW - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWTXPOW - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWRETRY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWRETRY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWENCODE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ENCODING_TOKEN_MAX, .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, }, [SIOCGIWENCODE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ENCODING_TOKEN_MAX, .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, }, [SIOCSIWPOWER - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWPOWER - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWGENIE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, [SIOCGIWGENIE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, [SIOCSIWAUTH - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWAUTH - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWENCODEEXT - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_encode_ext), .max_tokens = sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, }, [SIOCGIWENCODEEXT - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_encode_ext), .max_tokens = sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, }, [SIOCSIWPMKSA - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_pmksa), .max_tokens = sizeof(struct iw_pmksa), },};static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl);/* * Meta-data about all the additional standard Wireless Extension events * we know about. */static const struct iw_ioctl_description standard_event[] = { [IWEVTXDROP - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_ADDR, }, [IWEVQUAL - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_QUAL, }, [IWEVCUSTOM - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_CUSTOM_MAX, }, [IWEVREGISTERED - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_ADDR, }, [IWEVEXPIRED - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_ADDR, }, [IWEVGENIE - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, [IWEVMICHAELMICFAILURE - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_michaelmicfailure), }, [IWEVASSOCREQIE - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, [IWEVASSOCRESPIE - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, [IWEVPMKIDCAND - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_pmkid_cand), },};static const unsigned standard_event_num = ARRAY_SIZE(standard_event);/* Size (in bytes) of the various private data types */static const char iw_priv_type_size[] = { 0, /* IW_PRIV_TYPE_NONE */ 1, /* IW_PRIV_TYPE_BYTE */ 1, /* IW_PRIV_TYPE_CHAR */ 0, /* Not defined */ sizeof(__u32), /* IW_PRIV_TYPE_INT */ sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ 0, /* Not defined */};/* Size (in bytes) of various events */static const int event_type_size[] = { IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ 0, IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ 0, IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ 0, IW_EV_POINT_LEN, /* Without variable payload */ IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */};/* Size (in bytes) of various events, as packed */static const int event_type_pk_size[] = { IW_EV_LCP_PK_LEN, /* IW_HEADER_TYPE_NULL */ 0, IW_EV_CHAR_PK_LEN, /* IW_HEADER_TYPE_CHAR */ 0, IW_EV_UINT_PK_LEN, /* IW_HEADER_TYPE_UINT */ IW_EV_FREQ_PK_LEN, /* IW_HEADER_TYPE_FREQ */ IW_EV_ADDR_PK_LEN, /* IW_HEADER_TYPE_ADDR */ 0, IW_EV_POINT_PK_LEN, /* Without variable payload */ IW_EV_PARAM_PK_LEN, /* IW_HEADER_TYPE_PARAM */ IW_EV_QUAL_PK_LEN, /* IW_HEADER_TYPE_QUAL */};/************************ COMMON SUBROUTINES ************************//* * Stuff that may be used in various place or doesn't fit in one * of the section below. *//* ---------------------------------------------------------------- *//* * Return the driver handler associated with a specific Wireless Extension. */static iw_handler get_handler(struct net_device *dev, unsigned int cmd){ /* Don't "optimise" the following variable, it will crash */ unsigned int index; /* *MUST* be unsigned */ /* Check if we have some wireless handlers defined */ if (dev->wireless_handlers == NULL) return NULL; /* Try as a standard command */ index = cmd - SIOCIWFIRST; if (index < dev->wireless_handlers->num_standard) return dev->wireless_handlers->standard[index]; /* Try as a private command */ index = cmd - SIOCIWFIRSTPRIV; if (index < dev->wireless_handlers->num_private) return dev->wireless_handlers->private[index]; /* Not found */ return NULL;}/* ---------------------------------------------------------------- *//* * Get statistics out of the driver */static struct iw_statistics *get_wireless_stats(struct net_device *dev){ /* New location */ if ((dev->wireless_handlers != NULL) && (dev->wireless_handlers->get_wireless_stats != NULL)) return dev->wireless_handlers->get_wireless_stats(dev); /* Not found */ return NULL;}/* ---------------------------------------------------------------- *//* * Call the commit handler in the driver * (if exist and if conditions are right) * * Note : our current commit strategy is currently pretty dumb, * but we will be able to improve on that... * The goal is to try to agreagate as many changes as possible * before doing the commit. Drivers that will define a commit handler * are usually those that need a reset after changing parameters, so * we want to minimise the number of reset. * A cool idea is to use a timer : at each "set" command, we re-set the * timer, when the timer eventually fires, we call the driver. * Hopefully, more on that later. * * Also, I'm waiting to see how many people will complain about the * netif_running(dev) test. I'm open on that one... * Hopefully, the driver will remember to do a commit in "open()" ;-) */static int call_commit_handler(struct net_device *dev){ if ((netif_running(dev)) && (dev->wireless_handlers->standard[0] != NULL)) /* Call the commit handler on the driver */ return dev->wireless_handlers->standard[0](dev, NULL, NULL, NULL); else return 0; /* Command completed successfully */}/* ---------------------------------------------------------------- */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -