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