diff options
| author | Johannes Berg <johannes@sipsolutions.net> | 2009-09-29 23:27:28 +0200 | 
|---|---|---|
| committer | John W. Linville <linville@tuxdriver.com> | 2009-10-07 16:39:43 -0400 | 
| commit | 3d23e349d807177eaf519d444677cee86b1a04cf (patch) | |
| tree | e800dee247598e59185ddd2dd47d2ac6fe090b9c | |
| parent | bc974f4a230756faf4f69114f271de2e678b363b (diff) | |
| download | olio-linux-3.10-3d23e349d807177eaf519d444677cee86b1a04cf.tar.xz olio-linux-3.10-3d23e349d807177eaf519d444677cee86b1a04cf.zip  | |
wext: refactor
Refactor wext to
 * split out iwpriv handling
 * split out iwspy handling
 * split out procfs support
 * allow cfg80211 to have wireless extensions compat code
   w/o CONFIG_WIRELESS_EXT
After this, drivers need to
 - select WIRELESS_EXT	- for wext support
 - select WEXT_PRIV	- for iwpriv support
 - select WEXT_SPY	- for iwspy support
except cfg80211 -- which gets new hooks in wext-core.c
and can then get wext handlers without CONFIG_WIRELESS_EXT.
Wireless extensions procfs support is auto-selected
based on PROC_FS and anything that requires the wext core
(i.e. WIRELESS_EXT or CFG80211_WEXT).
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
| -rw-r--r-- | drivers/net/wireless/Kconfig | 29 | ||||
| -rw-r--r-- | drivers/net/wireless/hostap/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/net/wireless/ipw2x00/Kconfig | 5 | ||||
| -rw-r--r-- | drivers/net/wireless/orinoco/Kconfig | 2 | ||||
| -rw-r--r-- | include/net/cfg80211.h | 6 | ||||
| -rw-r--r-- | include/net/iw_handler.h | 14 | ||||
| -rw-r--r-- | include/net/net_namespace.h | 2 | ||||
| -rw-r--r-- | include/net/wext.h | 49 | ||||
| -rw-r--r-- | net/core/net-sysfs.c | 6 | ||||
| -rw-r--r-- | net/socket.c | 4 | ||||
| -rw-r--r-- | net/wireless/Kconfig | 50 | ||||
| -rw-r--r-- | net/wireless/Makefile | 8 | ||||
| -rw-r--r-- | net/wireless/core.c | 14 | ||||
| -rw-r--r-- | net/wireless/ibss.c | 10 | ||||
| -rw-r--r-- | net/wireless/mlme.c | 2 | ||||
| -rw-r--r-- | net/wireless/nl80211.c | 4 | ||||
| -rw-r--r-- | net/wireless/scan.c | 6 | ||||
| -rw-r--r-- | net/wireless/sme.c | 12 | ||||
| -rw-r--r-- | net/wireless/wext-core.c (renamed from net/wireless/wext.c) | 1452 | ||||
| -rw-r--r-- | net/wireless/wext-priv.c | 248 | ||||
| -rw-r--r-- | net/wireless/wext-proc.c | 155 | ||||
| -rw-r--r-- | net/wireless/wext-spy.c | 231 | 
22 files changed, 1157 insertions, 1154 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index d7a764a2fc1..c9829c59fd9 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -67,6 +67,8 @@ config WAVELAN  	tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support"  	depends on ISA && WLAN_PRE80211  	select WIRELESS_EXT +	select WEXT_SPY +	select WEXT_PRIV  	---help---  	  The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is  	  a Radio LAN (wireless Ethernet-like Local Area Network) using the @@ -90,6 +92,8 @@ config PCMCIA_WAVELAN  	tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support"  	depends on PCMCIA && WLAN_PRE80211  	select WIRELESS_EXT +	select WEXT_SPY +	select WEXT_PRIV  	help  	  Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA  	  (PC-card) wireless Ethernet networking card to your computer.  This @@ -102,6 +106,7 @@ config PCMCIA_NETWAVE  	tristate "Xircom Netwave AirSurfer Pcmcia wireless support"  	depends on PCMCIA && WLAN_PRE80211  	select WIRELESS_EXT +	select WEXT_PRIV  	help  	  Say Y here if you intend to attach this type of PCMCIA (PC-card)  	  wireless Ethernet networking card to your computer. @@ -123,6 +128,8 @@ config PCMCIA_RAYCS  	tristate "Aviator/Raytheon 2.4GHz wireless support"  	depends on PCMCIA && WLAN_80211  	select WIRELESS_EXT +	select WEXT_SPY +	select WEXT_PRIV  	---help---  	  Say Y here if you intend to attach an Aviator/Raytheon PCMCIA  	  (PC-card) wireless Ethernet networking card to your computer. @@ -136,6 +143,7 @@ config LIBERTAS  	tristate "Marvell 8xxx Libertas WLAN driver support"  	depends on WLAN_80211  	select WIRELESS_EXT +	select WEXT_SPY  	select LIB80211  	select FW_LOADER  	---help--- @@ -190,6 +198,8 @@ config AIRO  	depends on ISA_DMA_API && WLAN_80211 && (PCI || BROKEN)  	select WIRELESS_EXT  	select CRYPTO +	select WEXT_SPY +	select WEXT_PRIV  	---help---  	  This is the standard Linux driver to support Cisco/Aironet ISA and  	  PCI 802.11 wireless cards. @@ -207,6 +217,7 @@ config ATMEL        tristate "Atmel at76c50x chipset  802.11b support"        depends on (PCI || PCMCIA) && WLAN_80211        select WIRELESS_EXT +      select WEXT_PRIV        select FW_LOADER        select CRC32         ---help--- @@ -266,18 +277,21 @@ config AIRO_CS  	  Cisco Linux utilities can be used to configure the card.  config PCMCIA_WL3501 -      tristate "Planet WL3501 PCMCIA cards" -      depends on EXPERIMENTAL && PCMCIA && WLAN_80211 -      select WIRELESS_EXT -       ---help--- -         A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet. -	 It has basic support for Linux wireless extensions and initial -	 micro support for ethtool. +	tristate "Planet WL3501 PCMCIA cards" +	depends on EXPERIMENTAL && PCMCIA && WLAN_80211 +	select WIRELESS_EXT +	select WEXT_SPY +	help +	  A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet. +	  It has basic support for Linux wireless extensions and initial +	  micro support for ethtool.  config PRISM54  	tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus (DEPRECATED)'  	depends on PCI && EXPERIMENTAL && WLAN_80211  	select WIRELESS_EXT +	select WEXT_SPY +	select WEXT_PRIV  	select FW_LOADER  	---help---  	  This enables support for FullMAC PCI/Cardbus prism54 devices. This @@ -300,6 +314,7 @@ config USB_ZD1201  	tristate "USB ZD1201 based Wireless device support"  	depends on USB && WLAN_80211  	select WIRELESS_EXT +	select WEXT_PRIV  	select FW_LOADER  	---help---  	  Say Y if you want to use wireless LAN adapters based on the ZyDAS diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig index c15db229351..08f1e989653 100644 --- a/drivers/net/wireless/hostap/Kconfig +++ b/drivers/net/wireless/hostap/Kconfig @@ -2,6 +2,8 @@ config HOSTAP  	tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)"  	depends on WLAN_80211  	select WIRELESS_EXT +	select WEXT_SPY +	select WEXT_PRIV  	select CRYPTO  	select CRYPTO_ARC4  	select CRYPTO_ECB diff --git a/drivers/net/wireless/ipw2x00/Kconfig b/drivers/net/wireless/ipw2x00/Kconfig index a8131384c6b..56fab79dc36 100644 --- a/drivers/net/wireless/ipw2x00/Kconfig +++ b/drivers/net/wireless/ipw2x00/Kconfig @@ -6,6 +6,8 @@ config IPW2100  	tristate "Intel PRO/Wireless 2100 Network Connection"  	depends on PCI && WLAN_80211 && CFG80211  	select WIRELESS_EXT +	select WEXT_SPY +	select WEXT_PRIV  	select FW_LOADER  	select LIB80211  	select LIBIPW @@ -65,6 +67,8 @@ config IPW2200  	tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection"  	depends on PCI && WLAN_80211 && CFG80211  	select WIRELESS_EXT +	select WEXT_SPY +	select WEXT_PRIV  	select FW_LOADER  	select LIB80211  	select LIBIPW @@ -152,6 +156,7 @@ config LIBIPW  	tristate  	depends on PCI && WLAN_80211 && CFG80211  	select WIRELESS_EXT +	select WEXT_SPY  	select CRYPTO  	select CRYPTO_ARC4  	select CRYPTO_ECB diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig index 83b635fd778..13b03b3e8fc 100644 --- a/drivers/net/wireless/orinoco/Kconfig +++ b/drivers/net/wireless/orinoco/Kconfig @@ -3,6 +3,8 @@ config HERMES  	depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211  	depends on CFG80211  	select WIRELESS_EXT +	select WEXT_SPY +	select WEXT_PRIV  	select FW_LOADER  	select CRYPTO  	select CRYPTO_MICHAEL_MIC diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3d874c62021..241ea14d6df 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1171,6 +1171,10 @@ struct wiphy {  	struct net *_net;  #endif +#ifdef CONFIG_CFG80211_WEXT +	const struct iw_handler_def *wext; +#endif +  	char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));  }; @@ -1345,7 +1349,7 @@ struct wireless_dev {  	struct cfg80211_internal_bss *auth_bsses[MAX_AUTH_BSSES];  	struct cfg80211_internal_bss *current_bss; /* associated / joined */ -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	/* wext data */  	struct {  		struct cfg80211_ibss_params ibss; diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h index e9054a283fd..d5d337170a5 100644 --- a/include/net/iw_handler.h +++ b/include/net/iw_handler.h @@ -323,18 +323,19 @@ typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info,   */  struct iw_handler_def  { -	/* Number of handlers defined (more precisely, index of the -	 * last defined handler + 1) */ -	__u16			num_standard; -	__u16			num_private; -	/* Number of private arg description */ -	__u16			num_private_args;  	/* Array of handlers for standard ioctls  	 * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWCOMMIT]  	 */  	const iw_handler *	standard; +	/* Number of handlers defined (more precisely, index of the +	 * last defined handler + 1) */ +	__u16			num_standard; +#ifdef CONFIG_WEXT_PRIV +	__u16			num_private; +	/* Number of private arg description */ +	__u16			num_private_args;  	/* Array of handlers for private ioctls  	 * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV]  	 */ @@ -344,6 +345,7 @@ struct iw_handler_def  	 * can put it in any order you want and should not leave holes...  	 * We will automatically export that to user space... */  	const struct iw_priv_args *	private_args; +#endif  	/* New location of get_wireless_stats, to de-bloat struct net_device.  	 * The old pointer in struct net_device will be gradually phased diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index a1202841aad..699410142bf 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -80,7 +80,7 @@ struct net {  #ifdef CONFIG_XFRM  	struct netns_xfrm	xfrm;  #endif -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_WEXT_CORE  	struct sk_buff_head	wext_nlevents;  #endif  	struct net_generic	*gen; diff --git a/include/net/wext.h b/include/net/wext.h index 3f2b94de2cf..4f6e7423174 100644 --- a/include/net/wext.h +++ b/include/net/wext.h @@ -1,29 +1,19 @@  #ifndef __NET_WEXT_H  #define __NET_WEXT_H -/* - * wireless extensions interface to the core code - */ +#include <net/iw_handler.h>  struct net; -#ifdef CONFIG_WIRELESS_EXT -extern int wext_proc_init(struct net *net); -extern void wext_proc_exit(struct net *net); +#ifdef CONFIG_WEXT_CORE  extern int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,  			     void __user *arg);  extern int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,  				    unsigned long arg); +  extern struct iw_statistics *get_wireless_stats(struct net_device *dev); +extern int call_commit_handler(struct net_device *dev);  #else -static inline int wext_proc_init(struct net *net) -{ -	return 0; -} -static inline void wext_proc_exit(struct net *net) -{ -	return; -}  static inline int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,  				    void __user *arg)  { @@ -36,4 +26,35 @@ static inline int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,  }  #endif +#ifdef CONFIG_WEXT_PROC +extern int wext_proc_init(struct net *net); +extern void wext_proc_exit(struct net *net); +#else +static inline int wext_proc_init(struct net *net) +{ +	return 0; +} +static inline void wext_proc_exit(struct net *net) +{ +	return; +} +#endif + +#ifdef CONFIG_WEXT_PRIV +int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, +		       unsigned int cmd, struct iw_request_info *info, +		       iw_handler handler); +int compat_private_call(struct net_device *dev, struct iwreq *iwr, +			unsigned int cmd, struct iw_request_info *info, +			iw_handler handler); +int iw_handler_get_private(struct net_device *		dev, +			   struct iw_request_info *	info, +			   union iwreq_data *		wrqu, +			   char *			extra); +#else +#define ioctl_private_call NULL +#define compat_private_call NULL +#endif + +  #endif /* __NET_WEXT_H */ diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index effb78410eb..9b07535c288 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -543,8 +543,12 @@ int netdev_register_kobject(struct net_device *net)  	*groups++ = &netstat_group;  #ifdef CONFIG_WIRELESS_EXT_SYSFS -	if (net->wireless_handlers || net->ieee80211_ptr) +	if (net->ieee80211_ptr)  		*groups++ = &wireless_group; +#ifdef CONFIG_WIRELESS_EXT +	else if (net->wireless_handlers) +		*groups++ = &wireless_group; +#endif  #endif  #endif /* CONFIG_SYSFS */ diff --git a/net/socket.c b/net/socket.c index 75655365b5f..92a56709fd7 100644 --- a/net/socket.c +++ b/net/socket.c @@ -905,11 +905,11 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)  	if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {  		err = dev_ioctl(net, cmd, argp);  	} else -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_WEXT_CORE  	if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {  		err = dev_ioctl(net, cmd, argp);  	} else -#endif				/* CONFIG_WIRELESS_EXT */ +#endif  		switch (cmd) {  		case FIOSETOWN:  		case SIOCSPGRP: diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index abf7ca3f9ff..614bdcec1c8 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -1,3 +1,21 @@ +config WIRELESS_EXT +	bool + +config WEXT_CORE +	def_bool y +	depends on CFG80211_WEXT || WIRELESS_EXT + +config WEXT_PROC +	def_bool y +	depends on PROC_FS +	depends on WEXT_CORE + +config WEXT_SPY +	bool + +config WEXT_PRIV +	bool +  config CFG80211  	tristate "cfg80211 - wireless configuration API"  	depends on RFKILL || !RFKILL @@ -56,6 +74,12 @@ config CFG80211_REG_DEBUG  	  If unsure, say N. +config CFG80211_DEFAULT_PS_VALUE +	int +	default 1 if CFG80211_DEFAULT_PS +	default 0 +	depends on CFG80211 +  config CFG80211_DEFAULT_PS  	bool "enable powersave by default"  	depends on CFG80211 @@ -67,14 +91,10 @@ config CFG80211_DEFAULT_PS  	  applications instead -- they need to register their network  	  latency requirement, see Documentation/power/pm_qos_interface.txt. -config CFG80211_DEFAULT_PS_VALUE -	int -	default 1 if CFG80211_DEFAULT_PS -	default 0 -  config CFG80211_DEBUGFS  	bool "cfg80211 DebugFS entries" -	depends on CFG80211 && DEBUG_FS +	depends on CFG80211 +	depends on DEBUG_FS  	---help---  	  You can enable this if you want to debugfs entries for cfg80211. @@ -83,6 +103,7 @@ config CFG80211_DEBUGFS  config WIRELESS_OLD_REGULATORY  	bool "Old wireless static regulatory definitions"  	default n +	depends on CFG80211  	---help---  	  This option enables the old static regulatory information  	  and uses it within the new framework. This option is available @@ -94,20 +115,19 @@ config WIRELESS_OLD_REGULATORY  	  Say N and if you say Y, please tell us why. The default is N. -config WIRELESS_EXT -	bool "Wireless extensions" +config CFG80211_WEXT +	bool "cfg80211 wireless extensions compatibility" +	depends on CFG80211 +	select WEXT_CORE  	default y -	---help--- -	  This option enables the legacy wireless extensions -	  (wireless network interface configuration via ioctls.) - -	  Say Y unless you've upgraded all your userspace to use -	  nl80211 instead of wireless extensions. +	help +	  Enable this option if you need old userspace for wireless +	  extensions with cfg80211-based drivers.  config WIRELESS_EXT_SYSFS  	bool "Wireless extensions sysfs files"  	default y -	depends on WIRELESS_EXT && SYSFS +	depends on WEXT_CORE && SYSFS  	help  	  This option enables the deprecated wireless statistics  	  files in /sys/class/net/*/wireless/. The same information diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 3ecaa917997..c8141505a83 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -1,13 +1,17 @@ -obj-$(CONFIG_WIRELESS_EXT) += wext.o  obj-$(CONFIG_CFG80211) += cfg80211.o  obj-$(CONFIG_LIB80211) += lib80211.o  obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o  obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o  obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o +obj-$(CONFIG_WEXT_CORE) += wext-core.o +obj-$(CONFIG_WEXT_PROC) += wext-proc.o +obj-$(CONFIG_WEXT_SPY) += wext-spy.o +obj-$(CONFIG_WEXT_PRIV) += wext-priv.o +  cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o  cfg80211-y += mlme.o ibss.o sme.o chan.o  cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o -cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o wext-sme.o +cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o  ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/wireless/core.c b/net/wireless/core.c index e6f02e98e5f..eb0bb24b99c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -358,6 +358,10 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)  	INIT_LIST_HEAD(&rdev->bss_list);  	INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); +#ifdef CONFIG_CFG80211_WEXT +	rdev->wiphy.wext = &cfg80211_wext_handler; +#endif +  	device_initialize(&rdev->wiphy.dev);  	rdev->wiphy.dev.class = &ieee80211_class;  	rdev->wiphy.dev.platform_data = rdev; @@ -672,9 +676,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,  		wdev->netdev = dev;  		wdev->sme_state = CFG80211_SME_IDLE;  		mutex_unlock(&rdev->devlist_mtx); -#ifdef CONFIG_WIRELESS_EXT -		if (!dev->wireless_handlers) -			dev->wireless_handlers = &cfg80211_wext_handler; +#ifdef CONFIG_CFG80211_WEXT  		wdev->wext.default_key = -1;  		wdev->wext.default_mgmt_key = -1;  		wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; @@ -696,7 +698,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,  			break;  		case NL80211_IFTYPE_STATION:  			wdev_lock(wdev); -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  			kfree(wdev->wext.ie);  			wdev->wext.ie = NULL;  			wdev->wext.ie_len = 0; @@ -728,7 +730,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,  			mutex_unlock(&rdev->devlist_mtx);  			dev_put(dev);  		} -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  		cfg80211_lock_rdev(rdev);  		mutex_lock(&rdev->devlist_mtx);  		wdev_lock(wdev); @@ -766,7 +768,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,  			sysfs_remove_link(&dev->dev.kobj, "phy80211");  			list_del_init(&wdev->list);  			rdev->devlist_generation++; -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  			kfree(wdev->wext.keys);  #endif  		} diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index c8833891197..39b6d92e282 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -15,7 +15,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_bss *bss; -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	union iwreq_data wrqu;  #endif @@ -44,7 +44,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)  	nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid,  				GFP_KERNEL); -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	memset(&wrqu, 0, sizeof(wrqu));  	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);  	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); @@ -96,7 +96,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,  		kfree(wdev->connect_keys);  	wdev->connect_keys = connkeys; -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	wdev->wext.ibss.channel = params->channel;  #endif  	err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); @@ -154,7 +154,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)  	wdev->current_bss = NULL;  	wdev->ssid_len = 0; -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	if (!nowext)  		wdev->wext.ibss.ssid_len = 0;  #endif @@ -203,7 +203,7 @@ int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,  	return err;  } -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,  			    struct wireless_dev *wdev)  { diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 79d2eec54ce..ceb2c14c8f4 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -331,7 +331,7 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,  {  	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	union iwreq_data wrqu;  	char *buf = kmalloc(128, gfp); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e0ecc9f153d..14004e2ebd6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1264,7 +1264,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)  	if (!err)  		err = func(&rdev->wiphy, dev, key.idx); -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	if (!err) {  		if (func == rdev->ops->set_default_key)  			dev->ieee80211_ptr->wext.default_key = key.idx; @@ -1365,7 +1365,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)  	if (!err)  		err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	if (!err) {  		if (key.idx == dev->ieee80211_ptr->wext.default_key)  			dev->ieee80211_ptr->wext.default_key = -1; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index e5f92ee758f..2e8c515f3c5 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -22,7 +22,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)  {  	struct cfg80211_scan_request *request;  	struct net_device *dev; -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	union iwreq_data wrqu;  #endif @@ -47,7 +47,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)  	else  		nl80211_send_scan_done(rdev, dev); -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	if (!request->aborted) {  		memset(&wrqu, 0, sizeof(wrqu)); @@ -592,7 +592,7 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)  }  EXPORT_SYMBOL(cfg80211_unlink_bss); -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  int cfg80211_wext_siwscan(struct net_device *dev,  			  struct iw_request_info *info,  			  union iwreq_data *wrqu, char *extra) diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 93c3ed32920..d3624152f7f 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -345,7 +345,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	u8 *country_ie; -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	union iwreq_data wrqu;  #endif @@ -362,7 +362,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,  				    resp_ie, resp_ie_len,  				    status, GFP_KERNEL); -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	if (wextev) {  		if (req_ie && status == WLAN_STATUS_SUCCESS) {  			memset(&wrqu, 0, sizeof(wrqu)); @@ -477,7 +477,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid,  		       const u8 *resp_ie, size_t resp_ie_len)  {  	struct cfg80211_bss *bss; -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	union iwreq_data wrqu;  #endif @@ -512,7 +512,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid,  			    req_ie, req_ie_len, resp_ie, resp_ie_len,  			    GFP_KERNEL); -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	if (req_ie) {  		memset(&wrqu, 0, sizeof(wrqu));  		wrqu.data.length = req_ie_len; @@ -573,7 +573,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);  	int i; -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	union iwreq_data wrqu;  #endif @@ -631,7 +631,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,  		for (i = 0; i < 6; i++)  			rdev->ops->del_key(wdev->wiphy, dev, i, NULL); -#ifdef CONFIG_WIRELESS_EXT +#ifdef CONFIG_CFG80211_WEXT  	memset(&wrqu, 0, sizeof(wrqu));  	wrqu.ap_addr.sa_family = ARPHRD_ETHER;  	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); diff --git a/net/wireless/wext.c b/net/wireless/wext-core.c index 60fe57761ca..a4e5ddc8d4f 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext-core.c @@ -1,112 +1,28 @@  /* - * This file implement the Wireless Extensions APIs. + * This file implement the Wireless Extensions core API.   *   * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>   * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. + * Copyright	2009 Johannes Berg <johannes@sipsolutions.net>   *   * (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 lose 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 <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/rtnetlink.h> +#include <linux/wireless.h> +#include <linux/uaccess.h> +#include <net/cfg80211.h> +#include <net/iw_handler.h>  #include <net/netlink.h>  #include <net/wext.h> +#include <net/net_namespace.h> + +typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, +			       unsigned int, struct iw_request_info *, +			       iw_handler); -#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. @@ -390,18 +306,6 @@ static const struct iw_ioctl_description standard_event[] = {  };  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 */ @@ -433,323 +337,346 @@ static const int compat_event_type_size[] = {  };  #endif -/************************ 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]; +/* IW event code */ -	/* Not found */ -	return NULL; -} - -/* ---------------------------------------------------------------- */ -/* - * Get statistics out of the driver - */ -struct iw_statistics *get_wireless_stats(struct net_device *dev) +static int __net_init wext_pernet_init(struct net *net)  { -	/* 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; +	skb_queue_head_init(&net->wext_nlevents); +	return 0;  } -/* ---------------------------------------------------------------- */ -/* - * 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) +static void __net_exit wext_pernet_exit(struct net *net)  { -	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 */ +	skb_queue_purge(&net->wext_nlevents);  } -/* ---------------------------------------------------------------- */ -/* - * Calculate size of private arguments - */ -static int get_priv_size(__u16 args) -{ -	int	num = args & IW_PRIV_SIZE_MASK; -	int	type = (args & IW_PRIV_TYPE_MASK) >> 12; +static struct pernet_operations wext_pernet_ops = { +	.init = wext_pernet_init, +	.exit = wext_pernet_exit, +}; -	return num * iw_priv_type_size[type]; +static int __init wireless_nlevent_init(void) +{ +	return register_pernet_subsys(&wext_pernet_ops);  } -/* ---------------------------------------------------------------- */ -/* - * Re-calculate the size of private arguments - */ -static int adjust_priv_size(__u16 args, struct iw_point *iwp) +subsys_initcall(wireless_nlevent_init); + +/* Process events generated by the wireless layer or the driver. */ +static void wireless_nlevent_process(struct work_struct *work)  { -	int	num = iwp->length; -	int	max = args & IW_PRIV_SIZE_MASK; -	int	type = (args & IW_PRIV_TYPE_MASK) >> 12; +	struct sk_buff *skb; +	struct net *net; -	/* Make sure the driver doesn't goof up */ -	if (max < num) -		num = max; +	rtnl_lock(); + +	for_each_net(net) { +		while ((skb = skb_dequeue(&net->wext_nlevents))) +			rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, +				    GFP_KERNEL); +	} -	return num * iw_priv_type_size[type]; +	rtnl_unlock();  } -/* ---------------------------------------------------------------- */ -/* - * 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) +static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); + +static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, +					      struct sk_buff *skb)  { -	/* Get stats from the driver */ -	struct iw_statistics *stats; +	struct ifinfomsg *r; +	struct nlmsghdr  *nlh; -	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); +	nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); +	if (!nlh) +		return NULL; -		/* 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; +	r = nlmsg_data(nlh); +	r->ifi_family = AF_UNSPEC; +	r->__ifi_pad = 0; +	r->ifi_type = dev->type; +	r->ifi_index = dev->ifindex; +	r->ifi_flags = dev_get_flags(dev); +	r->ifi_change = 0;	/* Wireless changes don't affect those flags */ + +	NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); + +	return nlh; + nla_put_failure: +	nlmsg_cancel(skb, nlh); +	return NULL;  } -/* ---------------------------------------------------------------- */ +  /* - * Standard Wireless Handler : get iwpriv definitions - * Export the driver private handler definition - * They will be picked up by tools like iwpriv... + * Main event dispatcher. Called from other parts and drivers. + * Send the event on the appropriate channels. + * May be called from interrupt context.   */ -static int iw_handler_get_private(struct net_device *		dev, -				  struct iw_request_info *	info, -				  union iwreq_data *		wrqu, -				  char *			extra) +void wireless_send_event(struct net_device *	dev, +			 unsigned int		cmd, +			 union iwreq_data *	wrqu, +			 const 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; +	const struct iw_ioctl_description *	descr = NULL; +	int extra_len = 0; +	struct iw_event  *event;		/* Mallocated whole event */ +	int event_len;				/* Its size */ +	int hdr_len;				/* Size of the event header */ +	int wrqu_off = 0;			/* Offset in wrqu */ +	/* Don't "optimise" the following variable, it will crash */ +	unsigned	cmd_index;		/* *MUST* be unsigned */ +	struct sk_buff *skb; +	struct nlmsghdr *nlh; +	struct nlattr *nla; +#ifdef CONFIG_COMPAT +	struct __compat_iw_event *compat_event; +	struct compat_iw_point compat_wrqu; +	struct sk_buff *compskb; +#endif -	/* 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; +	/* +	 * Nothing in the kernel sends scan events with data, be safe. +	 * This is necessary because we cannot fix up scan event data +	 * for compat, due to being contained in 'extra', but normally +	 * applications are required to retrieve the scan data anyway +	 * and no data is included in the event, this codifies that +	 * practice. +	 */ +	if (WARN_ON(cmd == SIOCGIWSCAN && extra)) +		extra = NULL; + +	/* Get the description of the Event */ +	if (cmd <= SIOCIWLAST) { +		cmd_index = cmd - SIOCIWFIRST; +		if (cmd_index < standard_ioctl_num) +			descr = &(standard_ioctl[cmd_index]); +	} else { +		cmd_index = cmd - IWEVFIRST; +		if (cmd_index < standard_event_num) +			descr = &(standard_event[cmd_index]); +	} +	/* Don't accept unknown events */ +	if (descr == NULL) { +		/* Note : we don't return an error to the driver, because +		 * the driver would not know what to do about it. It can't +		 * return an error to the user, because the event is not +		 * initiated by a user request. +		 * The best the driver could do is to log an error message. +		 * We will do it ourselves instead... +		 */ +		printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n", +		       dev->name, cmd); +		return;  	} -	/* Set the number of available ioctls. */ -	wrqu->data.length = dev->wireless_handlers->num_private_args; +	/* Check extra parameters and set extra_len */ +	if (descr->header_type == IW_HEADER_TYPE_POINT) { +		/* Check if number of token fits within bounds */ +		if (wrqu->data.length > descr->max_tokens) { +			printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length); +			return; +		} +		if (wrqu->data.length < descr->min_tokens) { +			printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length); +			return; +		} +		/* Calculate extra_len - extra is NULL for restricted events */ +		if (extra != NULL) +			extra_len = wrqu->data.length * descr->token_size; +		/* Always at an offset in wrqu */ +		wrqu_off = IW_EV_POINT_OFF; +	} -	/* Copy structure to the user buffer. */ -	memcpy(extra, dev->wireless_handlers->private_args, -	       sizeof(struct iw_priv_args) * wrqu->data.length); +	/* Total length of the event */ +	hdr_len = event_type_size[descr->header_type]; +	event_len = hdr_len + extra_len; -	return 0; -} +	/* +	 * The problem for 64/32 bit. +	 * +	 * On 64-bit, a regular event is laid out as follows: +	 *      |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  | +	 *      | event.len | event.cmd |     p a d d i n g     | +	 *      | wrqu data ... (with the correct size)         | +	 * +	 * This padding exists because we manipulate event->u, +	 * and 'event' is not packed. +	 * +	 * An iw_point event is laid out like this instead: +	 *      |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  | +	 *      | event.len | event.cmd |     p a d d i n g     | +	 *      | iwpnt.len | iwpnt.flg |     p a d d i n g     | +	 *      | extra data  ... +	 * +	 * The second padding exists because struct iw_point is extended, +	 * but this depends on the platform... +	 * +	 * On 32-bit, all the padding shouldn't be there. +	 */ +	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); +	if (!skb) +		return; -/******************** /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". - */ +	/* Send via the RtNetlink event channel */ +	nlh = rtnetlink_ifinfo_prep(dev, skb); +	if (WARN_ON(!nlh)) { +		kfree_skb(skb); +		return; +	} -#ifdef CONFIG_PROC_FS +	/* Add the wireless events in the netlink packet */ +	nla = nla_reserve(skb, IFLA_WIRELESS, event_len); +	if (!nla) { +		kfree_skb(skb); +		return; +	} +	event = nla_data(nla); -/* ---------------------------------------------------------------- */ -/* - * 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); -	static struct iw_statistics nullstats = {}; +	/* Fill event - first clear to avoid data leaking */ +	memset(event, 0, hdr_len); +	event->len = event_len; +	event->cmd = cmd; +	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); +	if (extra_len) +		memcpy(((char *) event) + hdr_len, extra, extra_len); -	/* show device if it's wireless regardless of current stats */ -	if (!stats && dev->wireless_handlers) -		stats = &nullstats; +	nlmsg_end(skb, nlh); +#ifdef CONFIG_COMPAT +	hdr_len = compat_event_type_size[descr->header_type]; +	event_len = hdr_len + extra_len; -	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); +	compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); +	if (!compskb) { +		kfree_skb(skb); +		return; +	} -		if (stats != &nullstats) -			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; +	/* Send via the RtNetlink event channel */ +	nlh = rtnetlink_ifinfo_prep(dev, compskb); +	if (WARN_ON(!nlh)) { +		kfree_skb(skb); +		kfree_skb(compskb); +		return;  	} -} -/* ---------------------------------------------------------------- */ -/* - * Print info for /proc/net/wireless (print all entries) - */ -static int wireless_dev_seq_show(struct seq_file *seq, void *v) -{ -	might_sleep(); +	/* Add the wireless events in the netlink packet */ +	nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); +	if (!nla) { +		kfree_skb(skb); +		kfree_skb(compskb); +		return; +	} +	compat_event = nla_data(nla); -	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; +	compat_event->len = event_len; +	compat_event->cmd = cmd; +	if (descr->header_type == IW_HEADER_TYPE_POINT) { +		compat_wrqu.length = wrqu->data.length; +		compat_wrqu.flags = wrqu->data.flags; +		memcpy(&compat_event->pointer, +			((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, +			hdr_len - IW_EV_COMPAT_LCP_LEN); +		if (extra_len) +			memcpy(((char *) compat_event) + hdr_len, +				extra, extra_len); +	} else { +		/* extra_len must be zero, so no if (extra) needed */ +		memcpy(&compat_event->pointer, wrqu, +			hdr_len - IW_EV_COMPAT_LCP_LEN); +	} + +	nlmsg_end(compskb, nlh); + +	skb_shinfo(skb)->frag_list = compskb; +#endif +	skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); +	schedule_work(&wireless_nlevent_work);  } +EXPORT_SYMBOL(wireless_send_event); + + + +/* IW handlers */ -static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos) +struct iw_statistics *get_wireless_stats(struct net_device *dev)  { -	struct net *net = seq_file_net(seq); -	loff_t off; -	struct net_device *dev; +#ifdef CONFIG_WIRELESS_EXT +	if ((dev->wireless_handlers != NULL) && +	   (dev->wireless_handlers->get_wireless_stats != NULL)) +		return dev->wireless_handlers->get_wireless_stats(dev); +#endif -	rtnl_lock(); -	if (!*pos) -		return SEQ_START_TOKEN; +#ifdef CONFIG_CFG80211_WEXT +	if (dev->ieee80211_ptr && dev->ieee80211_ptr && +	    dev->ieee80211_ptr->wiphy && +	    dev->ieee80211_ptr->wiphy->wext && +	    dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) +		return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev); +#endif -	off = 1; -	for_each_netdev(net, dev) -		if (off++ == *pos) -			return dev; +	/* not found */  	return NULL;  } -static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) +static int iw_handler_get_iwstats(struct net_device *		dev, +				  struct iw_request_info *	info, +				  union iwreq_data *		wrqu, +				  char *			extra)  { -	struct net *net = seq_file_net(seq); +	/* Get stats from the driver */ +	struct iw_statistics *stats; -	++*pos; +	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); -	return v == SEQ_START_TOKEN ? -		first_net_device(net) : next_net_device(v); +		/* 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;  } -static void wireless_dev_seq_stop(struct seq_file *seq, void *v) +static iw_handler get_handler(struct net_device *dev, unsigned int cmd)  { -	rtnl_unlock(); -} - -static const struct seq_operations wireless_seq_ops = { -	.start = wireless_dev_seq_start, -	.next  = wireless_dev_seq_next, -	.stop  = wireless_dev_seq_stop, -	.show  = wireless_dev_seq_show, -}; +	/* Don't "optimise" the following variable, it will crash */ +	unsigned int	index;		/* *MUST* be unsigned */ +	const struct iw_handler_def *handlers = NULL; -static int seq_open_wireless(struct inode *inode, struct file *file) -{ -	return seq_open_net(inode, file, &wireless_seq_ops, -			    sizeof(struct seq_net_private)); -} +#ifdef CONFIG_CFG80211_WEXT +	if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) +		handlers = dev->ieee80211_ptr->wiphy->wext; +#endif +#ifdef CONFIG_WIRELESS_EXT +	if (dev->wireless_handlers) +		handlers = dev->wireless_handlers; +#endif -static const struct file_operations wireless_seq_fops = { -	.owner	 = THIS_MODULE, -	.open    = seq_open_wireless, -	.read    = seq_read, -	.llseek  = seq_lseek, -	.release = seq_release_net, -}; +	if (!handlers) +		return NULL; -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; +	/* Try as a standard command */ +	index = cmd - SIOCIWFIRST; +	if (index < handlers->num_standard) +		return handlers->standard[index]; -	return 0; -} +#ifdef CONFIG_WEXT_PRIV +	/* Try as a private command */ +	index = cmd - SIOCIWFIRSTPRIV; +	if (index < handlers->num_private) +		return handlers->private[index]; +#endif -void wext_proc_exit(struct net *net) -{ -	proc_net_remove(net, "wireless"); +	/* Not found */ +	return NULL;  } -#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. - */ -/* ---------------------------------------------------------------- */  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, @@ -893,188 +820,39 @@ out:  }  /* - * 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 iwreq		*iwr, -			       unsigned int		cmd, -			       struct iw_request_info	*info, -			       iw_handler		handler) -{ -	const struct iw_ioctl_description *	descr; -	int					ret = -EINVAL; - -	/* Get the description of the IOCTL */ -	if ((cmd - SIOCIWFIRST) >= standard_ioctl_num) -		return -EOPNOTSUPP; -	descr = &(standard_ioctl[cmd - SIOCIWFIRST]); - -	/* 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 { -		ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, -					      handler, dev, info); -	} - -	/* 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. + * Call the commit handler in the driver + * (if exist and if conditions are right)   * - * 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). + * 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 get_priv_descr_and_size(struct net_device *dev, unsigned int cmd, -				   const struct iw_priv_args **descrp) -{ -	const struct iw_priv_args *descr; -	int i, extra_size; - -	descr = NULL; -	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; -		} -	} - -	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 int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd, -				  const struct iw_priv_args *descr, -				  iw_handler handler, struct net_device *dev, -				  struct iw_request_info *info, int extra_size) +int call_commit_handler(struct net_device *dev)  { -	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 = kmalloc(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; -} - -static int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, -			      unsigned int cmd, struct iw_request_info *info, -			      iw_handler handler) -{ -	int extra_size = 0, ret = -EINVAL; -	const struct iw_priv_args *descr; - -	extra_size = get_priv_descr_and_size(dev, cmd, &descr); - -	/* 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 { -		ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr, -					     handler, dev, info, extra_size); -	} - -	/* Call commit handler if needed and defined */ -	if (ret == -EIWCOMMIT) -		ret = call_commit_handler(dev); - -	return ret; +#ifdef CONFIG_WIRELESS_EXT +	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 */ +#else +	/* cfg80211 has no commit */ +	return 0; +#endif  } -/* ---------------------------------------------------------------- */ -typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, -			       unsigned int, struct iw_request_info *, -			       iw_handler); -  /*   * Main IOCTl dispatcher.   * Check the type of IOCTL and call the appropriate wrapper... @@ -1103,9 +881,11 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,  		return standard(dev, iwr, cmd, info,  				&iw_handler_get_iwstats); +#ifdef CONFIG_WEXT_PRIV  	if (cmd == SIOCGIWPRIV && dev->wireless_handlers)  		return standard(dev, iwr, cmd, info, -				&iw_handler_get_private); +				iw_handler_get_private); +#endif  	/* Basic check */  	if (!netif_device_present(dev)) @@ -1117,7 +897,7 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,  		/* Standard and private are not the same */  		if (cmd < SIOCIWFIRSTPRIV)  			return standard(dev, iwr, cmd, info, handler); -		else +		else if (private)  			return private(dev, iwr, cmd, info, handler);  	}  	/* Old driver API : call driver ioctl handler */ @@ -1157,6 +937,50 @@ static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr,  	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 iwreq		*iwr, +			       unsigned int		cmd, +			       struct iw_request_info	*info, +			       iw_handler		handler) +{ +	const struct iw_ioctl_description *	descr; +	int					ret = -EINVAL; + +	/* Get the description of the IOCTL */ +	if ((cmd - SIOCIWFIRST) >= standard_ioctl_num) +		return -EOPNOTSUPP; +	descr = &(standard_ioctl[cmd - SIOCIWFIRST]); + +	/* 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 { +		ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, +					      handler, dev, info); +	} + +	/* 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 wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,  		      void __user *arg)  { @@ -1205,43 +1029,6 @@ static int compat_standard_call(struct net_device	*dev,  	return err;  } -static int compat_private_call(struct net_device *dev, struct iwreq *iwr, -			       unsigned int cmd, struct iw_request_info *info, -			       iw_handler handler) -{ -	const struct iw_priv_args *descr; -	int ret, extra_size; - -	extra_size = get_priv_descr_and_size(dev, cmd, &descr); - -	/* 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 { -		struct compat_iw_point *iwp_compat; -		struct iw_point iwp; - -		iwp_compat = (struct compat_iw_point *) &iwr->u.data; -		iwp.pointer = compat_ptr(iwp_compat->pointer); -		iwp.length = iwp_compat->length; -		iwp.flags = iwp_compat->flags; - -		ret = ioctl_private_iw_point(&iwp, cmd, descr, -					     handler, dev, info, extra_size); - -		iwp_compat->pointer = ptr_to_compat(iwp.pointer); -		iwp_compat->length = iwp.length; -		iwp_compat->flags = iwp.flags; -	} - -	/* Call commit handler if needed and defined */ -	if (ret == -EIWCOMMIT) -		ret = call_commit_handler(dev); - -	return ret; -} -  int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,  			     unsigned long arg)  { @@ -1274,502 +1061,3 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,  	return ret;  }  #endif - -static int __net_init wext_pernet_init(struct net *net) -{ -	skb_queue_head_init(&net->wext_nlevents); -	return 0; -} - -static void __net_exit wext_pernet_exit(struct net *net) -{ -	skb_queue_purge(&net->wext_nlevents); -} - -static struct pernet_operations wext_pernet_ops = { -	.init = wext_pernet_init, -	.exit = wext_pernet_exit, -}; - -static int __init wireless_nlevent_init(void) -{ -	return register_pernet_subsys(&wext_pernet_ops); -} - -subsys_initcall(wireless_nlevent_init); - -/* Process events generated by the wireless layer or the driver. */ -static void wireless_nlevent_process(struct work_struct *work) -{ -	struct sk_buff *skb; -	struct net *net; - -	rtnl_lock(); - -	for_each_net(net) { -		while ((skb = skb_dequeue(&net->wext_nlevents))) -			rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, -				    GFP_KERNEL); -	} - -	rtnl_unlock(); -} - -static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); - -static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, -					      struct sk_buff *skb) -{ -	struct ifinfomsg *r; -	struct nlmsghdr  *nlh; - -	nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); -	if (!nlh) -		return NULL; - -	r = nlmsg_data(nlh); -	r->ifi_family = AF_UNSPEC; -	r->__ifi_pad = 0; -	r->ifi_type = dev->type; -	r->ifi_index = dev->ifindex; -	r->ifi_flags = dev_get_flags(dev); -	r->ifi_change = 0;	/* Wireless changes don't affect those flags */ - -	NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); - -	return nlh; - nla_put_failure: -	nlmsg_cancel(skb, nlh); -	return NULL; -} - - -/* - * Main event dispatcher. Called from other parts and drivers. - * Send the event on the appropriate channels. - * May be called from interrupt context. - */ -void wireless_send_event(struct net_device *	dev, -			 unsigned int		cmd, -			 union iwreq_data *	wrqu, -			 const char *		extra) -{ -	const struct iw_ioctl_description *	descr = NULL; -	int extra_len = 0; -	struct iw_event  *event;		/* Mallocated whole event */ -	int event_len;				/* Its size */ -	int hdr_len;				/* Size of the event header */ -	int wrqu_off = 0;			/* Offset in wrqu */ -	/* Don't "optimise" the following variable, it will crash */ -	unsigned	cmd_index;		/* *MUST* be unsigned */ -	struct sk_buff *skb; -	struct nlmsghdr *nlh; -	struct nlattr *nla; -#ifdef CONFIG_COMPAT -	struct __compat_iw_event *compat_event; -	struct compat_iw_point compat_wrqu; -	struct sk_buff *compskb; -#endif - -	/* -	 * Nothing in the kernel sends scan events with data, be safe. -	 * This is necessary because we cannot fix up scan event data -	 * for compat, due to being contained in 'extra', but normally -	 * applications are required to retrieve the scan data anyway -	 * and no data is included in the event, this codifies that -	 * practice. -	 */ -	if (WARN_ON(cmd == SIOCGIWSCAN && extra)) -		extra = NULL; - -	/* Get the description of the Event */ -	if (cmd <= SIOCIWLAST) { -		cmd_index = cmd - SIOCIWFIRST; -		if (cmd_index < standard_ioctl_num) -			descr = &(standard_ioctl[cmd_index]); -	} else { -		cmd_index = cmd - IWEVFIRST; -		if (cmd_index < standard_event_num) -			descr = &(standard_event[cmd_index]); -	} -	/* Don't accept unknown events */ -	if (descr == NULL) { -		/* Note : we don't return an error to the driver, because -		 * the driver would not know what to do about it. It can't -		 * return an error to the user, because the event is not -		 * initiated by a user request. -		 * The best the driver could do is to log an error message. -		 * We will do it ourselves instead... -		 */ -		printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n", -		       dev->name, cmd); -		return; -	} - -	/* Check extra parameters and set extra_len */ -	if (descr->header_type == IW_HEADER_TYPE_POINT) { -		/* Check if number of token fits within bounds */ -		if (wrqu->data.length > descr->max_tokens) { -			printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length); -			return; -		} -		if (wrqu->data.length < descr->min_tokens) { -			printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length); -			return; -		} -		/* Calculate extra_len - extra is NULL for restricted events */ -		if (extra != NULL) -			extra_len = wrqu->data.length * descr->token_size; -		/* Always at an offset in wrqu */ -		wrqu_off = IW_EV_POINT_OFF; -	} - -	/* Total length of the event */ -	hdr_len = event_type_size[descr->header_type]; -	event_len = hdr_len + extra_len; - -	/* -	 * The problem for 64/32 bit. -	 * -	 * On 64-bit, a regular event is laid out as follows: -	 *      |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  | -	 *      | event.len | event.cmd |     p a d d i n g     | -	 *      | wrqu data ... (with the correct size)         | -	 * -	 * This padding exists because we manipulate event->u, -	 * and 'event' is not packed. -	 * -	 * An iw_point event is laid out like this instead: -	 *      |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  | -	 *      | event.len | event.cmd |     p a d d i n g     | -	 *      | iwpnt.len | iwpnt.flg |     p a d d i n g     | -	 *      | extra data  ... -	 * -	 * The second padding exists because struct iw_point is extended, -	 * but this depends on the platform... -	 * -	 * On 32-bit, all the padding shouldn't be there. -	 */ - -	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); -	if (!skb) -		return; - -	/* Send via the RtNetlink event channel */ -	nlh = rtnetlink_ifinfo_prep(dev, skb); -	if (WARN_ON(!nlh)) { -		kfree_skb(skb); -		return; -	} - -	/* Add the wireless events in the netlink packet */ -	nla = nla_reserve(skb, IFLA_WIRELESS, event_len); -	if (!nla) { -		kfree_skb(skb); -		return; -	} -	event = nla_data(nla); - -	/* Fill event - first clear to avoid data leaking */ -	memset(event, 0, hdr_len); -	event->len = event_len; -	event->cmd = cmd; -	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); -	if (extra_len) -		memcpy(((char *) event) + hdr_len, extra, extra_len); - -	nlmsg_end(skb, nlh); -#ifdef CONFIG_COMPAT -	hdr_len = compat_event_type_size[descr->header_type]; -	event_len = hdr_len + extra_len; - -	compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); -	if (!compskb) { -		kfree_skb(skb); -		return; -	} - -	/* Send via the RtNetlink event channel */ -	nlh = rtnetlink_ifinfo_prep(dev, compskb); -	if (WARN_ON(!nlh)) { -		kfree_skb(skb); -		kfree_skb(compskb); -		return; -	} - -	/* Add the wireless events in the netlink packet */ -	nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); -	if (!nla) { -		kfree_skb(skb); -		kfree_skb(compskb); -		return; -	} -	compat_event = nla_data(nla); - -	compat_event->len = event_len; -	compat_event->cmd = cmd; -	if (descr->header_type == IW_HEADER_TYPE_POINT) { -		compat_wrqu.length = wrqu->data.length; -		compat_wrqu.flags = wrqu->data.flags; -		memcpy(&compat_event->pointer, -			((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, -			hdr_len - IW_EV_COMPAT_LCP_LEN); -		if (extra_len) -			memcpy(((char *) compat_event) + hdr_len, -				extra, extra_len); -	} else { -		/* extra_len must be zero, so no if (extra) needed */ -		memcpy(&compat_event->pointer, wrqu, -			hdr_len - IW_EV_COMPAT_LCP_LEN); -	} - -	nlmsg_end(compskb, nlh); - -	skb_shinfo(skb)->frag_list = compskb; -#endif -	skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); -	schedule_work(&wireless_nlevent_work); -} -EXPORT_SYMBOL(wireless_send_event); - -/********************** ENHANCED IWSPY SUPPORT **********************/ -/* - * In the old days, the driver was handling spy support all by itself. - * Now, the driver can delegate this task to Wireless Extensions. - * It needs to use those standard spy iw_handler in struct iw_handler_def, - * push data to us via wireless_spy_update() and include struct iw_spy_data - * in its private part (and export it in net_device->wireless_data->spy_data). - * One of the main advantage of centralising spy support here is that - * it becomes much easier to improve and extend it without having to touch - * the drivers. One example is the addition of the Spy-Threshold events. - */ - -/* ---------------------------------------------------------------- */ -/* - * Return the pointer to the spy data in the driver. - * Because this is called on the Rx path via wireless_spy_update(), - * we want it to be efficient... - */ -static inline struct iw_spy_data *get_spydata(struct net_device *dev) -{ -	/* This is the new way */ -	if (dev->wireless_data) -		return dev->wireless_data->spy_data; -	return NULL; -} - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : set Spy List - */ -int iw_handler_set_spy(struct net_device *	dev, -		       struct iw_request_info *	info, -		       union iwreq_data *	wrqu, -		       char *			extra) -{ -	struct iw_spy_data *	spydata = get_spydata(dev); -	struct sockaddr *	address = (struct sockaddr *) extra; - -	/* Make sure driver is not buggy or using the old API */ -	if (!spydata) -		return -EOPNOTSUPP; - -	/* Disable spy collection while we copy the addresses. -	 * While we copy addresses, any call to wireless_spy_update() -	 * will NOP. This is OK, as anyway the addresses are changing. */ -	spydata->spy_number = 0; - -	/* We want to operate without locking, because wireless_spy_update() -	 * most likely will happen in the interrupt handler, and therefore -	 * have its own locking constraints and needs performance. -	 * The rtnl_lock() make sure we don't race with the other iw_handlers. -	 * This make sure wireless_spy_update() "see" that the spy list -	 * is temporarily disabled. */ -	smp_wmb(); - -	/* Are there are addresses to copy? */ -	if (wrqu->data.length > 0) { -		int i; - -		/* Copy addresses */ -		for (i = 0; i < wrqu->data.length; i++) -			memcpy(spydata->spy_address[i], address[i].sa_data, -			       ETH_ALEN); -		/* Reset stats */ -		memset(spydata->spy_stat, 0, -		       sizeof(struct iw_quality) * IW_MAX_SPY); -	} - -	/* Make sure above is updated before re-enabling */ -	smp_wmb(); - -	/* Enable addresses */ -	spydata->spy_number = wrqu->data.length; - -	return 0; -} -EXPORT_SYMBOL(iw_handler_set_spy); - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : get Spy List - */ -int iw_handler_get_spy(struct net_device *	dev, -		       struct iw_request_info *	info, -		       union iwreq_data *	wrqu, -		       char *			extra) -{ -	struct iw_spy_data *	spydata = get_spydata(dev); -	struct sockaddr *	address = (struct sockaddr *) extra; -	int			i; - -	/* Make sure driver is not buggy or using the old API */ -	if (!spydata) -		return -EOPNOTSUPP; - -	wrqu->data.length = spydata->spy_number; - -	/* Copy addresses. */ -	for (i = 0; i < spydata->spy_number; i++) 	{ -		memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); -		address[i].sa_family = AF_UNIX; -	} -	/* Copy stats to the user buffer (just after). */ -	if (spydata->spy_number > 0) -		memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number), -		       spydata->spy_stat, -		       sizeof(struct iw_quality) * spydata->spy_number); -	/* Reset updated flags. */ -	for (i = 0; i < spydata->spy_number; i++) -		spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED; -	return 0; -} -EXPORT_SYMBOL(iw_handler_get_spy); - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : set spy threshold - */ -int iw_handler_set_thrspy(struct net_device *	dev, -			  struct iw_request_info *info, -			  union iwreq_data *	wrqu, -			  char *		extra) -{ -	struct iw_spy_data *	spydata = get_spydata(dev); -	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra; - -	/* Make sure driver is not buggy or using the old API */ -	if (!spydata) -		return -EOPNOTSUPP; - -	/* Just do it */ -	memcpy(&(spydata->spy_thr_low), &(threshold->low), -	       2 * sizeof(struct iw_quality)); - -	/* Clear flag */ -	memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); - -	return 0; -} -EXPORT_SYMBOL(iw_handler_set_thrspy); - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : get spy threshold - */ -int iw_handler_get_thrspy(struct net_device *	dev, -			  struct iw_request_info *info, -			  union iwreq_data *	wrqu, -			  char *		extra) -{ -	struct iw_spy_data *	spydata = get_spydata(dev); -	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra; - -	/* Make sure driver is not buggy or using the old API */ -	if (!spydata) -		return -EOPNOTSUPP; - -	/* Just do it */ -	memcpy(&(threshold->low), &(spydata->spy_thr_low), -	       2 * sizeof(struct iw_quality)); - -	return 0; -} -EXPORT_SYMBOL(iw_handler_get_thrspy); - -/*------------------------------------------------------------------*/ -/* - * Prepare and send a Spy Threshold event - */ -static void iw_send_thrspy_event(struct net_device *	dev, -				 struct iw_spy_data *	spydata, -				 unsigned char *	address, -				 struct iw_quality *	wstats) -{ -	union iwreq_data	wrqu; -	struct iw_thrspy	threshold; - -	/* Init */ -	wrqu.data.length = 1; -	wrqu.data.flags = 0; -	/* Copy address */ -	memcpy(threshold.addr.sa_data, address, ETH_ALEN); -	threshold.addr.sa_family = ARPHRD_ETHER; -	/* Copy stats */ -	memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); -	/* Copy also thresholds */ -	memcpy(&(threshold.low), &(spydata->spy_thr_low), -	       2 * sizeof(struct iw_quality)); - -	/* Send event to user space */ -	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); -} - -/* ---------------------------------------------------------------- */ -/* - * Call for the driver to update the spy data. - * For now, the spy data is a simple array. As the size of the array is - * small, this is good enough. If we wanted to support larger number of - * spy addresses, we should use something more efficient... - */ -void wireless_spy_update(struct net_device *	dev, -			 unsigned char *	address, -			 struct iw_quality *	wstats) -{ -	struct iw_spy_data *	spydata = get_spydata(dev); -	int			i; -	int			match = -1; - -	/* Make sure driver is not buggy or using the old API */ -	if (!spydata) -		return; - -	/* Update all records that match */ -	for (i = 0; i < spydata->spy_number; i++) -		if (!compare_ether_addr(address, spydata->spy_address[i])) { -			memcpy(&(spydata->spy_stat[i]), wstats, -			       sizeof(struct iw_quality)); -			match = i; -		} - -	/* Generate an event if we cross the spy threshold. -	 * To avoid event storms, we have a simple hysteresis : we generate -	 * event only when we go under the low threshold or above the -	 * high threshold. */ -	if (match >= 0) { -		if (spydata->spy_thr_under[match]) { -			if (wstats->level > spydata->spy_thr_high.level) { -				spydata->spy_thr_under[match] = 0; -				iw_send_thrspy_event(dev, spydata, -						     address, wstats); -			} -		} else { -			if (wstats->level < spydata->spy_thr_low.level) { -				spydata->spy_thr_under[match] = 1; -				iw_send_thrspy_event(dev, spydata, -						     address, wstats); -			} -		} -	} -} -EXPORT_SYMBOL(wireless_spy_update); diff --git a/net/wireless/wext-priv.c b/net/wireless/wext-priv.c new file mode 100644 index 00000000000..a3c2277de9e --- /dev/null +++ b/net/wireless/wext-priv.c @@ -0,0 +1,248 @@ +/* + * This file implement the Wireless Extensions priv API. + * + * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com> + * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. + * Copyright	2009 Johannes Berg <johannes@sipsolutions.net> + * + * (As all part of the Linux kernel, this file is GPL) + */ +#include <linux/wireless.h> +#include <linux/netdevice.h> +#include <net/iw_handler.h> +#include <net/wext.h> + +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; +} + +/* 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 */ +}; + +static 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]; +} + +static int adjust_priv_size(__u16 args, struct iw_point *iwp) +{ +	int	num = iwp->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]; +} + +/* + * 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 get_priv_descr_and_size(struct net_device *dev, unsigned int cmd, +				   const struct iw_priv_args **descrp) +{ +	const struct iw_priv_args *descr; +	int i, extra_size; + +	descr = NULL; +	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; +		} +	} + +	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 int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd, +				  const struct iw_priv_args *descr, +				  iw_handler handler, struct net_device *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 = kmalloc(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; +} + +int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, +		       unsigned int cmd, struct iw_request_info *info, +		       iw_handler handler) +{ +	int extra_size = 0, ret = -EINVAL; +	const struct iw_priv_args *descr; + +	extra_size = get_priv_descr_and_size(dev, cmd, &descr); + +	/* 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 { +		ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr, +					     handler, dev, info, extra_size); +	} + +	/* Call commit handler if needed and defined */ +	if (ret == -EIWCOMMIT) +		ret = call_commit_handler(dev); + +	return ret; +} + +#ifdef CONFIG_COMPAT +int compat_private_call(struct net_device *dev, struct iwreq *iwr, +			unsigned int cmd, struct iw_request_info *info, +			iw_handler handler) +{ +	const struct iw_priv_args *descr; +	int ret, extra_size; + +	extra_size = get_priv_descr_and_size(dev, cmd, &descr); + +	/* 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 { +		struct compat_iw_point *iwp_compat; +		struct iw_point iwp; + +		iwp_compat = (struct compat_iw_point *) &iwr->u.data; +		iwp.pointer = compat_ptr(iwp_compat->pointer); +		iwp.length = iwp_compat->length; +		iwp.flags = iwp_compat->flags; + +		ret = ioctl_private_iw_point(&iwp, cmd, descr, +					     handler, dev, info, extra_size); + +		iwp_compat->pointer = ptr_to_compat(iwp.pointer); +		iwp_compat->length = iwp.length; +		iwp_compat->flags = iwp.flags; +	} + +	/* Call commit handler if needed and defined */ +	if (ret == -EIWCOMMIT) +		ret = call_commit_handler(dev); + +	return ret; +} +#endif diff --git a/net/wireless/wext-proc.c b/net/wireless/wext-proc.c new file mode 100644 index 00000000000..273a7f77c83 --- /dev/null +++ b/net/wireless/wext-proc.c @@ -0,0 +1,155 @@ +/* + * This file implement the Wireless Extensions proc API. + * + * 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) + */ + +/* + * 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". + */ + +#include <linux/module.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/wireless.h> +#include <linux/netdevice.h> +#include <linux/rtnetlink.h> +#include <net/iw_handler.h> +#include <net/wext.h> + + +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); +	static struct iw_statistics nullstats = {}; + +	/* show device if it's wireless regardless of current stats */ +	if (!stats) { +#ifdef CONFIG_WIRELESS_EXT +		if (dev->wireless_handlers) +			stats = &nullstats; +#endif +#ifdef CONFIG_CFG80211 +		if (dev->ieee80211_ptr) +			stats = &nullstats; +#endif +	} + +	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); + +		if (stats != &nullstats) +			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; +	} +} + +/* ---------------------------------------------------------------- */ +/* + * Print info for /proc/net/wireless (print all entries) + */ +static int wireless_dev_seq_show(struct seq_file *seq, void *v) +{ +	might_sleep(); + +	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 void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos) +{ +	struct net *net = seq_file_net(seq); +	loff_t off; +	struct net_device *dev; + +	rtnl_lock(); +	if (!*pos) +		return SEQ_START_TOKEN; + +	off = 1; +	for_each_netdev(net, dev) +		if (off++ == *pos) +			return dev; +	return NULL; +} + +static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ +	struct net *net = seq_file_net(seq); + +	++*pos; + +	return v == SEQ_START_TOKEN ? +		first_net_device(net) : next_net_device(v); +} + +static void wireless_dev_seq_stop(struct seq_file *seq, void *v) +{ +	rtnl_unlock(); +} + +static const struct seq_operations wireless_seq_ops = { +	.start = wireless_dev_seq_start, +	.next  = wireless_dev_seq_next, +	.stop  = wireless_dev_seq_stop, +	.show  = wireless_dev_seq_show, +}; + +static int seq_open_wireless(struct inode *inode, struct file *file) +{ +	return seq_open_net(inode, file, &wireless_seq_ops, +			    sizeof(struct seq_net_private)); +} + +static const struct file_operations wireless_seq_fops = { +	.owner	 = THIS_MODULE, +	.open    = seq_open_wireless, +	.read    = seq_read, +	.llseek  = seq_lseek, +	.release = seq_release_net, +}; + +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"); +} diff --git a/net/wireless/wext-spy.c b/net/wireless/wext-spy.c new file mode 100644 index 00000000000..6dcfe65a2d1 --- /dev/null +++ b/net/wireless/wext-spy.c @@ -0,0 +1,231 @@ +/* + * This file implement the Wireless Extensions spy API. + * + * 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) + */ + +#include <linux/wireless.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <net/iw_handler.h> +#include <net/arp.h> +#include <net/wext.h> + +static inline struct iw_spy_data *get_spydata(struct net_device *dev) +{ +	/* This is the new way */ +	if (dev->wireless_data) +		return dev->wireless_data->spy_data; +	return NULL; +} + +int iw_handler_set_spy(struct net_device *	dev, +		       struct iw_request_info *	info, +		       union iwreq_data *	wrqu, +		       char *			extra) +{ +	struct iw_spy_data *	spydata = get_spydata(dev); +	struct sockaddr *	address = (struct sockaddr *) extra; + +	/* Make sure driver is not buggy or using the old API */ +	if (!spydata) +		return -EOPNOTSUPP; + +	/* Disable spy collection while we copy the addresses. +	 * While we copy addresses, any call to wireless_spy_update() +	 * will NOP. This is OK, as anyway the addresses are changing. */ +	spydata->spy_number = 0; + +	/* We want to operate without locking, because wireless_spy_update() +	 * most likely will happen in the interrupt handler, and therefore +	 * have its own locking constraints and needs performance. +	 * The rtnl_lock() make sure we don't race with the other iw_handlers. +	 * This make sure wireless_spy_update() "see" that the spy list +	 * is temporarily disabled. */ +	smp_wmb(); + +	/* Are there are addresses to copy? */ +	if (wrqu->data.length > 0) { +		int i; + +		/* Copy addresses */ +		for (i = 0; i < wrqu->data.length; i++) +			memcpy(spydata->spy_address[i], address[i].sa_data, +			       ETH_ALEN); +		/* Reset stats */ +		memset(spydata->spy_stat, 0, +		       sizeof(struct iw_quality) * IW_MAX_SPY); +	} + +	/* Make sure above is updated before re-enabling */ +	smp_wmb(); + +	/* Enable addresses */ +	spydata->spy_number = wrqu->data.length; + +	return 0; +} +EXPORT_SYMBOL(iw_handler_set_spy); + +int iw_handler_get_spy(struct net_device *	dev, +		       struct iw_request_info *	info, +		       union iwreq_data *	wrqu, +		       char *			extra) +{ +	struct iw_spy_data *	spydata = get_spydata(dev); +	struct sockaddr *	address = (struct sockaddr *) extra; +	int			i; + +	/* Make sure driver is not buggy or using the old API */ +	if (!spydata) +		return -EOPNOTSUPP; + +	wrqu->data.length = spydata->spy_number; + +	/* Copy addresses. */ +	for (i = 0; i < spydata->spy_number; i++) 	{ +		memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); +		address[i].sa_family = AF_UNIX; +	} +	/* Copy stats to the user buffer (just after). */ +	if (spydata->spy_number > 0) +		memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number), +		       spydata->spy_stat, +		       sizeof(struct iw_quality) * spydata->spy_number); +	/* Reset updated flags. */ +	for (i = 0; i < spydata->spy_number; i++) +		spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED; +	return 0; +} +EXPORT_SYMBOL(iw_handler_get_spy); + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : set spy threshold + */ +int iw_handler_set_thrspy(struct net_device *	dev, +			  struct iw_request_info *info, +			  union iwreq_data *	wrqu, +			  char *		extra) +{ +	struct iw_spy_data *	spydata = get_spydata(dev); +	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra; + +	/* Make sure driver is not buggy or using the old API */ +	if (!spydata) +		return -EOPNOTSUPP; + +	/* Just do it */ +	memcpy(&(spydata->spy_thr_low), &(threshold->low), +	       2 * sizeof(struct iw_quality)); + +	/* Clear flag */ +	memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); + +	return 0; +} +EXPORT_SYMBOL(iw_handler_set_thrspy); + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : get spy threshold + */ +int iw_handler_get_thrspy(struct net_device *	dev, +			  struct iw_request_info *info, +			  union iwreq_data *	wrqu, +			  char *		extra) +{ +	struct iw_spy_data *	spydata = get_spydata(dev); +	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra; + +	/* Make sure driver is not buggy or using the old API */ +	if (!spydata) +		return -EOPNOTSUPP; + +	/* Just do it */ +	memcpy(&(threshold->low), &(spydata->spy_thr_low), +	       2 * sizeof(struct iw_quality)); + +	return 0; +} +EXPORT_SYMBOL(iw_handler_get_thrspy); + +/*------------------------------------------------------------------*/ +/* + * Prepare and send a Spy Threshold event + */ +static void iw_send_thrspy_event(struct net_device *	dev, +				 struct iw_spy_data *	spydata, +				 unsigned char *	address, +				 struct iw_quality *	wstats) +{ +	union iwreq_data	wrqu; +	struct iw_thrspy	threshold; + +	/* Init */ +	wrqu.data.length = 1; +	wrqu.data.flags = 0; +	/* Copy address */ +	memcpy(threshold.addr.sa_data, address, ETH_ALEN); +	threshold.addr.sa_family = ARPHRD_ETHER; +	/* Copy stats */ +	memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); +	/* Copy also thresholds */ +	memcpy(&(threshold.low), &(spydata->spy_thr_low), +	       2 * sizeof(struct iw_quality)); + +	/* Send event to user space */ +	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); +} + +/* ---------------------------------------------------------------- */ +/* + * Call for the driver to update the spy data. + * For now, the spy data is a simple array. As the size of the array is + * small, this is good enough. If we wanted to support larger number of + * spy addresses, we should use something more efficient... + */ +void wireless_spy_update(struct net_device *	dev, +			 unsigned char *	address, +			 struct iw_quality *	wstats) +{ +	struct iw_spy_data *	spydata = get_spydata(dev); +	int			i; +	int			match = -1; + +	/* Make sure driver is not buggy or using the old API */ +	if (!spydata) +		return; + +	/* Update all records that match */ +	for (i = 0; i < spydata->spy_number; i++) +		if (!compare_ether_addr(address, spydata->spy_address[i])) { +			memcpy(&(spydata->spy_stat[i]), wstats, +			       sizeof(struct iw_quality)); +			match = i; +		} + +	/* Generate an event if we cross the spy threshold. +	 * To avoid event storms, we have a simple hysteresis : we generate +	 * event only when we go under the low threshold or above the +	 * high threshold. */ +	if (match >= 0) { +		if (spydata->spy_thr_under[match]) { +			if (wstats->level > spydata->spy_thr_high.level) { +				spydata->spy_thr_under[match] = 0; +				iw_send_thrspy_event(dev, spydata, +						     address, wstats); +			} +		} else { +			if (wstats->level < spydata->spy_thr_low.level) { +				spydata->spy_thr_under[match] = 1; +				iw_send_thrspy_event(dev, spydata, +						     address, wstats); +			} +		} +	} +} +EXPORT_SYMBOL(wireless_spy_update);  |