xref: /linux/net/wireless/wext-spy.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
13d23e349SJohannes Berg /*
23d23e349SJohannes Berg  * This file implement the Wireless Extensions spy API.
33d23e349SJohannes Berg  *
43d23e349SJohannes Berg  * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
53d23e349SJohannes Berg  * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
63d23e349SJohannes Berg  *
73d23e349SJohannes Berg  * (As all part of the Linux kernel, this file is GPL)
83d23e349SJohannes Berg  */
93d23e349SJohannes Berg 
103d23e349SJohannes Berg #include <linux/wireless.h>
113d23e349SJohannes Berg #include <linux/netdevice.h>
123d23e349SJohannes Berg #include <linux/etherdevice.h>
13bc3b2d7fSPaul Gortmaker #include <linux/export.h>
143d23e349SJohannes Berg #include <net/iw_handler.h>
153d23e349SJohannes Berg #include <net/arp.h>
163d23e349SJohannes Berg #include <net/wext.h>
173d23e349SJohannes Berg 
get_spydata(struct net_device * dev)183d23e349SJohannes Berg static inline struct iw_spy_data *get_spydata(struct net_device *dev)
193d23e349SJohannes Berg {
203d23e349SJohannes Berg 	/* This is the new way */
213d23e349SJohannes Berg 	if (dev->wireless_data)
223d23e349SJohannes Berg 		return dev->wireless_data->spy_data;
233d23e349SJohannes Berg 	return NULL;
243d23e349SJohannes Berg }
253d23e349SJohannes Berg 
iw_handler_set_spy(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)263d23e349SJohannes Berg int iw_handler_set_spy(struct net_device *	dev,
273d23e349SJohannes Berg 		       struct iw_request_info *	info,
283d23e349SJohannes Berg 		       union iwreq_data *	wrqu,
293d23e349SJohannes Berg 		       char *			extra)
303d23e349SJohannes Berg {
313d23e349SJohannes Berg 	struct iw_spy_data *	spydata = get_spydata(dev);
323d23e349SJohannes Berg 	struct sockaddr *	address = (struct sockaddr *) extra;
333d23e349SJohannes Berg 
343d23e349SJohannes Berg 	/* Make sure driver is not buggy or using the old API */
353d23e349SJohannes Berg 	if (!spydata)
363d23e349SJohannes Berg 		return -EOPNOTSUPP;
373d23e349SJohannes Berg 
383d23e349SJohannes Berg 	/* Disable spy collection while we copy the addresses.
393d23e349SJohannes Berg 	 * While we copy addresses, any call to wireless_spy_update()
403d23e349SJohannes Berg 	 * will NOP. This is OK, as anyway the addresses are changing. */
413d23e349SJohannes Berg 	spydata->spy_number = 0;
423d23e349SJohannes Berg 
433d23e349SJohannes Berg 	/* We want to operate without locking, because wireless_spy_update()
443d23e349SJohannes Berg 	 * most likely will happen in the interrupt handler, and therefore
453d23e349SJohannes Berg 	 * have its own locking constraints and needs performance.
463d23e349SJohannes Berg 	 * The rtnl_lock() make sure we don't race with the other iw_handlers.
473d23e349SJohannes Berg 	 * This make sure wireless_spy_update() "see" that the spy list
483d23e349SJohannes Berg 	 * is temporarily disabled. */
493d23e349SJohannes Berg 	smp_wmb();
503d23e349SJohannes Berg 
513d23e349SJohannes Berg 	/* Are there are addresses to copy? */
523d23e349SJohannes Berg 	if (wrqu->data.length > 0) {
533d23e349SJohannes Berg 		int i;
543d23e349SJohannes Berg 
553d23e349SJohannes Berg 		/* Copy addresses */
563d23e349SJohannes Berg 		for (i = 0; i < wrqu->data.length; i++)
573d23e349SJohannes Berg 			memcpy(spydata->spy_address[i], address[i].sa_data,
583d23e349SJohannes Berg 			       ETH_ALEN);
593d23e349SJohannes Berg 		/* Reset stats */
603d23e349SJohannes Berg 		memset(spydata->spy_stat, 0,
613d23e349SJohannes Berg 		       sizeof(struct iw_quality) * IW_MAX_SPY);
623d23e349SJohannes Berg 	}
633d23e349SJohannes Berg 
643d23e349SJohannes Berg 	/* Make sure above is updated before re-enabling */
653d23e349SJohannes Berg 	smp_wmb();
663d23e349SJohannes Berg 
673d23e349SJohannes Berg 	/* Enable addresses */
683d23e349SJohannes Berg 	spydata->spy_number = wrqu->data.length;
693d23e349SJohannes Berg 
703d23e349SJohannes Berg 	return 0;
713d23e349SJohannes Berg }
723d23e349SJohannes Berg EXPORT_SYMBOL(iw_handler_set_spy);
733d23e349SJohannes Berg 
iw_handler_get_spy(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)743d23e349SJohannes Berg int iw_handler_get_spy(struct net_device *	dev,
753d23e349SJohannes Berg 		       struct iw_request_info *	info,
763d23e349SJohannes Berg 		       union iwreq_data *	wrqu,
773d23e349SJohannes Berg 		       char *			extra)
783d23e349SJohannes Berg {
793d23e349SJohannes Berg 	struct iw_spy_data *	spydata = get_spydata(dev);
803d23e349SJohannes Berg 	struct sockaddr *	address = (struct sockaddr *) extra;
813d23e349SJohannes Berg 	int			i;
823d23e349SJohannes Berg 
833d23e349SJohannes Berg 	/* Make sure driver is not buggy or using the old API */
843d23e349SJohannes Berg 	if (!spydata)
853d23e349SJohannes Berg 		return -EOPNOTSUPP;
863d23e349SJohannes Berg 
873d23e349SJohannes Berg 	wrqu->data.length = spydata->spy_number;
883d23e349SJohannes Berg 
893d23e349SJohannes Berg 	/* Copy addresses. */
903d23e349SJohannes Berg 	for (i = 0; i < spydata->spy_number; i++) 	{
913d23e349SJohannes Berg 		memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
923d23e349SJohannes Berg 		address[i].sa_family = AF_UNIX;
933d23e349SJohannes Berg 	}
943d23e349SJohannes Berg 	/* Copy stats to the user buffer (just after). */
953d23e349SJohannes Berg 	if (spydata->spy_number > 0)
963d23e349SJohannes Berg 		memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
973d23e349SJohannes Berg 		       spydata->spy_stat,
983d23e349SJohannes Berg 		       sizeof(struct iw_quality) * spydata->spy_number);
993d23e349SJohannes Berg 	/* Reset updated flags. */
1003d23e349SJohannes Berg 	for (i = 0; i < spydata->spy_number; i++)
1013d23e349SJohannes Berg 		spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
1023d23e349SJohannes Berg 	return 0;
1033d23e349SJohannes Berg }
1043d23e349SJohannes Berg EXPORT_SYMBOL(iw_handler_get_spy);
1053d23e349SJohannes Berg 
1063d23e349SJohannes Berg /*------------------------------------------------------------------*/
1073d23e349SJohannes Berg /*
1083d23e349SJohannes Berg  * Standard Wireless Handler : set spy threshold
1093d23e349SJohannes Berg  */
iw_handler_set_thrspy(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)1103d23e349SJohannes Berg int iw_handler_set_thrspy(struct net_device *	dev,
1113d23e349SJohannes Berg 			  struct iw_request_info *info,
1123d23e349SJohannes Berg 			  union iwreq_data *	wrqu,
1133d23e349SJohannes Berg 			  char *		extra)
1143d23e349SJohannes Berg {
1153d23e349SJohannes Berg 	struct iw_spy_data *	spydata = get_spydata(dev);
1163d23e349SJohannes Berg 	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
1173d23e349SJohannes Berg 
1183d23e349SJohannes Berg 	/* Make sure driver is not buggy or using the old API */
1193d23e349SJohannes Berg 	if (!spydata)
1203d23e349SJohannes Berg 		return -EOPNOTSUPP;
1213d23e349SJohannes Berg 
1223d23e349SJohannes Berg 	/* Just do it */
123*e93bdd78SGustavo A. R. Silva 	spydata->spy_thr_low = threshold->low;
124*e93bdd78SGustavo A. R. Silva 	spydata->spy_thr_high = threshold->high;
1253d23e349SJohannes Berg 
1263d23e349SJohannes Berg 	/* Clear flag */
1273d23e349SJohannes Berg 	memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
1283d23e349SJohannes Berg 
1293d23e349SJohannes Berg 	return 0;
1303d23e349SJohannes Berg }
1313d23e349SJohannes Berg EXPORT_SYMBOL(iw_handler_set_thrspy);
1323d23e349SJohannes Berg 
1333d23e349SJohannes Berg /*------------------------------------------------------------------*/
1343d23e349SJohannes Berg /*
1353d23e349SJohannes Berg  * Standard Wireless Handler : get spy threshold
1363d23e349SJohannes Berg  */
iw_handler_get_thrspy(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)1373d23e349SJohannes Berg int iw_handler_get_thrspy(struct net_device *	dev,
1383d23e349SJohannes Berg 			  struct iw_request_info *info,
1393d23e349SJohannes Berg 			  union iwreq_data *	wrqu,
1403d23e349SJohannes Berg 			  char *		extra)
1413d23e349SJohannes Berg {
1423d23e349SJohannes Berg 	struct iw_spy_data *	spydata = get_spydata(dev);
1433d23e349SJohannes Berg 	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
1443d23e349SJohannes Berg 
1453d23e349SJohannes Berg 	/* Make sure driver is not buggy or using the old API */
1463d23e349SJohannes Berg 	if (!spydata)
1473d23e349SJohannes Berg 		return -EOPNOTSUPP;
1483d23e349SJohannes Berg 
1493d23e349SJohannes Berg 	/* Just do it */
150*e93bdd78SGustavo A. R. Silva 	threshold->low = spydata->spy_thr_low;
151*e93bdd78SGustavo A. R. Silva 	threshold->high = spydata->spy_thr_high;
1523d23e349SJohannes Berg 
1533d23e349SJohannes Berg 	return 0;
1543d23e349SJohannes Berg }
1553d23e349SJohannes Berg EXPORT_SYMBOL(iw_handler_get_thrspy);
1563d23e349SJohannes Berg 
1573d23e349SJohannes Berg /*------------------------------------------------------------------*/
1583d23e349SJohannes Berg /*
1593d23e349SJohannes Berg  * Prepare and send a Spy Threshold event
1603d23e349SJohannes Berg  */
iw_send_thrspy_event(struct net_device * dev,struct iw_spy_data * spydata,unsigned char * address,struct iw_quality * wstats)1613d23e349SJohannes Berg static void iw_send_thrspy_event(struct net_device *	dev,
1623d23e349SJohannes Berg 				 struct iw_spy_data *	spydata,
1633d23e349SJohannes Berg 				 unsigned char *	address,
1643d23e349SJohannes Berg 				 struct iw_quality *	wstats)
1653d23e349SJohannes Berg {
1663d23e349SJohannes Berg 	union iwreq_data	wrqu;
1673d23e349SJohannes Berg 	struct iw_thrspy	threshold;
1683d23e349SJohannes Berg 
1693d23e349SJohannes Berg 	/* Init */
1703d23e349SJohannes Berg 	wrqu.data.length = 1;
1713d23e349SJohannes Berg 	wrqu.data.flags = 0;
1723d23e349SJohannes Berg 	/* Copy address */
1733d23e349SJohannes Berg 	memcpy(threshold.addr.sa_data, address, ETH_ALEN);
1743d23e349SJohannes Berg 	threshold.addr.sa_family = ARPHRD_ETHER;
1753d23e349SJohannes Berg 	/* Copy stats */
176*e93bdd78SGustavo A. R. Silva 	threshold.qual = *wstats;
1773d23e349SJohannes Berg 	/* Copy also thresholds */
178*e93bdd78SGustavo A. R. Silva 	threshold.low = spydata->spy_thr_low;
179*e93bdd78SGustavo A. R. Silva 	threshold.high = spydata->spy_thr_high;
1803d23e349SJohannes Berg 
1813d23e349SJohannes Berg 	/* Send event to user space */
1823d23e349SJohannes Berg 	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
1833d23e349SJohannes Berg }
1843d23e349SJohannes Berg 
1853d23e349SJohannes Berg /* ---------------------------------------------------------------- */
1863d23e349SJohannes Berg /*
1873d23e349SJohannes Berg  * Call for the driver to update the spy data.
1883d23e349SJohannes Berg  * For now, the spy data is a simple array. As the size of the array is
1893d23e349SJohannes Berg  * small, this is good enough. If we wanted to support larger number of
1903d23e349SJohannes Berg  * spy addresses, we should use something more efficient...
1913d23e349SJohannes Berg  */
wireless_spy_update(struct net_device * dev,unsigned char * address,struct iw_quality * wstats)1923d23e349SJohannes Berg void wireless_spy_update(struct net_device *	dev,
1933d23e349SJohannes Berg 			 unsigned char *	address,
1943d23e349SJohannes Berg 			 struct iw_quality *	wstats)
1953d23e349SJohannes Berg {
1963d23e349SJohannes Berg 	struct iw_spy_data *	spydata = get_spydata(dev);
1973d23e349SJohannes Berg 	int			i;
1983d23e349SJohannes Berg 	int			match = -1;
1993d23e349SJohannes Berg 
2003d23e349SJohannes Berg 	/* Make sure driver is not buggy or using the old API */
2013d23e349SJohannes Berg 	if (!spydata)
2023d23e349SJohannes Berg 		return;
2033d23e349SJohannes Berg 
2043d23e349SJohannes Berg 	/* Update all records that match */
2053d23e349SJohannes Berg 	for (i = 0; i < spydata->spy_number; i++)
206ac422d3cSJoe Perches 		if (ether_addr_equal(address, spydata->spy_address[i])) {
2073d23e349SJohannes Berg 			memcpy(&(spydata->spy_stat[i]), wstats,
2083d23e349SJohannes Berg 			       sizeof(struct iw_quality));
2093d23e349SJohannes Berg 			match = i;
2103d23e349SJohannes Berg 		}
2113d23e349SJohannes Berg 
2123d23e349SJohannes Berg 	/* Generate an event if we cross the spy threshold.
2133d23e349SJohannes Berg 	 * To avoid event storms, we have a simple hysteresis : we generate
2143d23e349SJohannes Berg 	 * event only when we go under the low threshold or above the
2153d23e349SJohannes Berg 	 * high threshold. */
2163d23e349SJohannes Berg 	if (match >= 0) {
2173d23e349SJohannes Berg 		if (spydata->spy_thr_under[match]) {
2183d23e349SJohannes Berg 			if (wstats->level > spydata->spy_thr_high.level) {
2193d23e349SJohannes Berg 				spydata->spy_thr_under[match] = 0;
2203d23e349SJohannes Berg 				iw_send_thrspy_event(dev, spydata,
2213d23e349SJohannes Berg 						     address, wstats);
2223d23e349SJohannes Berg 			}
2233d23e349SJohannes Berg 		} else {
2243d23e349SJohannes Berg 			if (wstats->level < spydata->spy_thr_low.level) {
2253d23e349SJohannes Berg 				spydata->spy_thr_under[match] = 1;
2263d23e349SJohannes Berg 				iw_send_thrspy_event(dev, spydata,
2273d23e349SJohannes Berg 						     address, wstats);
2283d23e349SJohannes Berg 			}
2293d23e349SJohannes Berg 		}
2303d23e349SJohannes Berg 	}
2313d23e349SJohannes Berg }
2323d23e349SJohannes Berg EXPORT_SYMBOL(wireless_spy_update);
233