xref: /openbmc/linux/net/wireless/wext-spy.c (revision 41e0d491)
1 /*
2  * This file implement the Wireless Extensions spy API.
3  *
4  * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
5  * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
6  *
7  * (As all part of the Linux kernel, this file is GPL)
8  */
9 
10 #include <linux/wireless.h>
11 #include <linux/netdevice.h>
12 #include <linux/etherdevice.h>
13 #include <linux/export.h>
14 #include <net/iw_handler.h>
15 #include <net/arp.h>
16 #include <net/wext.h>
17 
18 static inline struct iw_spy_data *get_spydata(struct net_device *dev)
19 {
20 	/* This is the new way */
21 	if (dev->wireless_data)
22 		return dev->wireless_data->spy_data;
23 	return NULL;
24 }
25 
26 int iw_handler_set_spy(struct net_device *	dev,
27 		       struct iw_request_info *	info,
28 		       union iwreq_data *	wrqu,
29 		       char *			extra)
30 {
31 	struct iw_spy_data *	spydata = get_spydata(dev);
32 	struct sockaddr *	address = (struct sockaddr *) extra;
33 
34 	/* Make sure driver is not buggy or using the old API */
35 	if (!spydata)
36 		return -EOPNOTSUPP;
37 
38 	/* Disable spy collection while we copy the addresses.
39 	 * While we copy addresses, any call to wireless_spy_update()
40 	 * will NOP. This is OK, as anyway the addresses are changing. */
41 	spydata->spy_number = 0;
42 
43 	/* We want to operate without locking, because wireless_spy_update()
44 	 * most likely will happen in the interrupt handler, and therefore
45 	 * have its own locking constraints and needs performance.
46 	 * The rtnl_lock() make sure we don't race with the other iw_handlers.
47 	 * This make sure wireless_spy_update() "see" that the spy list
48 	 * is temporarily disabled. */
49 	smp_wmb();
50 
51 	/* Are there are addresses to copy? */
52 	if (wrqu->data.length > 0) {
53 		int i;
54 
55 		/* Copy addresses */
56 		for (i = 0; i < wrqu->data.length; i++)
57 			memcpy(spydata->spy_address[i], address[i].sa_data,
58 			       ETH_ALEN);
59 		/* Reset stats */
60 		memset(spydata->spy_stat, 0,
61 		       sizeof(struct iw_quality) * IW_MAX_SPY);
62 	}
63 
64 	/* Make sure above is updated before re-enabling */
65 	smp_wmb();
66 
67 	/* Enable addresses */
68 	spydata->spy_number = wrqu->data.length;
69 
70 	return 0;
71 }
72 EXPORT_SYMBOL(iw_handler_set_spy);
73 
74 int iw_handler_get_spy(struct net_device *	dev,
75 		       struct iw_request_info *	info,
76 		       union iwreq_data *	wrqu,
77 		       char *			extra)
78 {
79 	struct iw_spy_data *	spydata = get_spydata(dev);
80 	struct sockaddr *	address = (struct sockaddr *) extra;
81 	int			i;
82 
83 	/* Make sure driver is not buggy or using the old API */
84 	if (!spydata)
85 		return -EOPNOTSUPP;
86 
87 	wrqu->data.length = spydata->spy_number;
88 
89 	/* Copy addresses. */
90 	for (i = 0; i < spydata->spy_number; i++) 	{
91 		memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
92 		address[i].sa_family = AF_UNIX;
93 	}
94 	/* Copy stats to the user buffer (just after). */
95 	if (spydata->spy_number > 0)
96 		memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
97 		       spydata->spy_stat,
98 		       sizeof(struct iw_quality) * spydata->spy_number);
99 	/* Reset updated flags. */
100 	for (i = 0; i < spydata->spy_number; i++)
101 		spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
102 	return 0;
103 }
104 EXPORT_SYMBOL(iw_handler_get_spy);
105 
106 /*------------------------------------------------------------------*/
107 /*
108  * Standard Wireless Handler : set spy threshold
109  */
110 int iw_handler_set_thrspy(struct net_device *	dev,
111 			  struct iw_request_info *info,
112 			  union iwreq_data *	wrqu,
113 			  char *		extra)
114 {
115 	struct iw_spy_data *	spydata = get_spydata(dev);
116 	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
117 
118 	/* Make sure driver is not buggy or using the old API */
119 	if (!spydata)
120 		return -EOPNOTSUPP;
121 
122 	/* Just do it */
123 	spydata->spy_thr_low = threshold->low;
124 	spydata->spy_thr_high = threshold->high;
125 
126 	/* Clear flag */
127 	memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
128 
129 	return 0;
130 }
131 EXPORT_SYMBOL(iw_handler_set_thrspy);
132 
133 /*------------------------------------------------------------------*/
134 /*
135  * Standard Wireless Handler : get spy threshold
136  */
137 int iw_handler_get_thrspy(struct net_device *	dev,
138 			  struct iw_request_info *info,
139 			  union iwreq_data *	wrqu,
140 			  char *		extra)
141 {
142 	struct iw_spy_data *	spydata = get_spydata(dev);
143 	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
144 
145 	/* Make sure driver is not buggy or using the old API */
146 	if (!spydata)
147 		return -EOPNOTSUPP;
148 
149 	/* Just do it */
150 	threshold->low = spydata->spy_thr_low;
151 	threshold->high = spydata->spy_thr_high;
152 
153 	return 0;
154 }
155 EXPORT_SYMBOL(iw_handler_get_thrspy);
156 
157 /*------------------------------------------------------------------*/
158 /*
159  * Prepare and send a Spy Threshold event
160  */
161 static void iw_send_thrspy_event(struct net_device *	dev,
162 				 struct iw_spy_data *	spydata,
163 				 unsigned char *	address,
164 				 struct iw_quality *	wstats)
165 {
166 	union iwreq_data	wrqu;
167 	struct iw_thrspy	threshold;
168 
169 	/* Init */
170 	wrqu.data.length = 1;
171 	wrqu.data.flags = 0;
172 	/* Copy address */
173 	memcpy(threshold.addr.sa_data, address, ETH_ALEN);
174 	threshold.addr.sa_family = ARPHRD_ETHER;
175 	/* Copy stats */
176 	threshold.qual = *wstats;
177 	/* Copy also thresholds */
178 	threshold.low = spydata->spy_thr_low;
179 	threshold.high = spydata->spy_thr_high;
180 
181 	/* Send event to user space */
182 	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
183 }
184 
185 /* ---------------------------------------------------------------- */
186 /*
187  * Call for the driver to update the spy data.
188  * For now, the spy data is a simple array. As the size of the array is
189  * small, this is good enough. If we wanted to support larger number of
190  * spy addresses, we should use something more efficient...
191  */
192 void wireless_spy_update(struct net_device *	dev,
193 			 unsigned char *	address,
194 			 struct iw_quality *	wstats)
195 {
196 	struct iw_spy_data *	spydata = get_spydata(dev);
197 	int			i;
198 	int			match = -1;
199 
200 	/* Make sure driver is not buggy or using the old API */
201 	if (!spydata)
202 		return;
203 
204 	/* Update all records that match */
205 	for (i = 0; i < spydata->spy_number; i++)
206 		if (ether_addr_equal(address, spydata->spy_address[i])) {
207 			memcpy(&(spydata->spy_stat[i]), wstats,
208 			       sizeof(struct iw_quality));
209 			match = i;
210 		}
211 
212 	/* Generate an event if we cross the spy threshold.
213 	 * To avoid event storms, we have a simple hysteresis : we generate
214 	 * event only when we go under the low threshold or above the
215 	 * high threshold. */
216 	if (match >= 0) {
217 		if (spydata->spy_thr_under[match]) {
218 			if (wstats->level > spydata->spy_thr_high.level) {
219 				spydata->spy_thr_under[match] = 0;
220 				iw_send_thrspy_event(dev, spydata,
221 						     address, wstats);
222 			}
223 		} else {
224 			if (wstats->level < spydata->spy_thr_low.level) {
225 				spydata->spy_thr_under[match] = 1;
226 				iw_send_thrspy_event(dev, spydata,
227 						     address, wstats);
228 			}
229 		}
230 	}
231 }
232 EXPORT_SYMBOL(wireless_spy_update);
233