Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
wext-spy.c
Go to the documentation of this file.
1 /*
2  * This file implement the Wireless Extensions spy API.
3  *
4  * Authors : Jean Tourrilhes - HPL - <[email protected]>
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 }
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 }
105 
106 /*------------------------------------------------------------------*/
107 /*
108  * Standard Wireless Handler : set spy threshold
109  */
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  memcpy(&(spydata->spy_thr_low), &(threshold->low),
124  2 * sizeof(struct iw_quality));
125 
126  /* Clear flag */
127  memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
128 
129  return 0;
130 }
132 
133 /*------------------------------------------------------------------*/
134 /*
135  * Standard Wireless Handler : get spy threshold
136  */
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  memcpy(&(threshold->low), &(spydata->spy_thr_low),
151  2 * sizeof(struct iw_quality));
152 
153  return 0;
154 }
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  memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
177  /* Copy also thresholds */
178  memcpy(&(threshold.low), &(spydata->spy_thr_low),
179  2 * sizeof(struct iw_quality));
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 }