Blame view

net/wireless/scan.c 33.1 KB
2a5193119   Johannes Berg   cfg80211/nl80211:...
1
2
3
4
5
6
  /*
   * cfg80211 scan result handling
   *
   * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
   */
  #include <linux/kernel.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
7
  #include <linux/slab.h>
2a5193119   Johannes Berg   cfg80211/nl80211:...
8
9
10
11
12
13
14
  #include <linux/module.h>
  #include <linux/netdevice.h>
  #include <linux/wireless.h>
  #include <linux/nl80211.h>
  #include <linux/etherdevice.h>
  #include <net/arp.h>
  #include <net/cfg80211.h>
262eb9b22   Johannes Berg   cfg80211: split w...
15
  #include <net/cfg80211-wext.h>
2a5193119   Johannes Berg   cfg80211/nl80211:...
16
17
18
  #include <net/iw_handler.h>
  #include "core.h"
  #include "nl80211.h"
a9a11622c   Johannes Berg   cfg80211: self-co...
19
  #include "wext-compat.h"
2a5193119   Johannes Berg   cfg80211/nl80211:...
20

09f97e0fc   Helmut Schaa   cfg80211: increas...
21
  #define IEEE80211_SCAN_RESULT_EXPIRE	(15 * HZ)
2a5193119   Johannes Berg   cfg80211/nl80211:...
22

01a0ac417   Johannes Berg   cfg80211: check l...
23
  void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
2a5193119   Johannes Berg   cfg80211/nl80211:...
24
  {
667503ddc   Johannes Berg   cfg80211: fix loc...
25
  	struct cfg80211_scan_request *request;
2a5193119   Johannes Berg   cfg80211/nl80211:...
26
  	struct net_device *dev;
3d23e349d   Johannes Berg   wext: refactor
27
  #ifdef CONFIG_CFG80211_WEXT
2a5193119   Johannes Berg   cfg80211/nl80211:...
28
29
  	union iwreq_data wrqu;
  #endif
01a0ac417   Johannes Berg   cfg80211: check l...
30
  	ASSERT_RDEV_LOCK(rdev);
667503ddc   Johannes Berg   cfg80211: fix loc...
31
  	request = rdev->scan_req;
01a0ac417   Johannes Berg   cfg80211: check l...
32
33
  	if (!request)
  		return;
463d01832   Johannes Berg   cfg80211: make aw...
34
  	dev = request->dev;
2a5193119   Johannes Berg   cfg80211/nl80211:...
35

6829c878e   Johannes Berg   cfg80211: emulate...
36
37
38
39
40
41
  	/*
  	 * This must be before sending the other events!
  	 * Otherwise, wpa_supplicant gets completely confused with
  	 * wext events.
  	 */
  	cfg80211_sme_scan_done(dev);
667503ddc   Johannes Berg   cfg80211: fix loc...
42
  	if (request->aborted)
36e6fea84   Johannes Berg   cfg80211: check f...
43
  		nl80211_send_scan_aborted(rdev, dev);
2a5193119   Johannes Berg   cfg80211/nl80211:...
44
  	else
36e6fea84   Johannes Berg   cfg80211: check f...
45
  		nl80211_send_scan_done(rdev, dev);
2a5193119   Johannes Berg   cfg80211/nl80211:...
46

3d23e349d   Johannes Berg   wext: refactor
47
  #ifdef CONFIG_CFG80211_WEXT
667503ddc   Johannes Berg   cfg80211: fix loc...
48
  	if (!request->aborted) {
2a5193119   Johannes Berg   cfg80211/nl80211:...
49
50
51
52
53
54
55
  		memset(&wrqu, 0, sizeof(wrqu));
  
  		wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
  	}
  #endif
  
  	dev_put(dev);
36e6fea84   Johannes Berg   cfg80211: check f...
56
  	rdev->scan_req = NULL;
01a0ac417   Johannes Berg   cfg80211: check l...
57
58
59
60
61
62
63
64
65
66
67
  
  	/*
  	 * OK. If this is invoked with "leak" then we can't
  	 * free this ... but we've cleaned it up anyway. The
  	 * driver failed to call the scan_done callback, so
  	 * all bets are off, it might still be trying to use
  	 * the scan request or not ... if it accesses the dev
  	 * in there (it shouldn't anyway) then it may crash.
  	 */
  	if (!leak)
  		kfree(request);
2a5193119   Johannes Berg   cfg80211/nl80211:...
68
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
69

36e6fea84   Johannes Berg   cfg80211: check f...
70
71
72
73
74
75
76
77
  void __cfg80211_scan_done(struct work_struct *wk)
  {
  	struct cfg80211_registered_device *rdev;
  
  	rdev = container_of(wk, struct cfg80211_registered_device,
  			    scan_done_wk);
  
  	cfg80211_lock_rdev(rdev);
01a0ac417   Johannes Berg   cfg80211: check l...
78
  	___cfg80211_scan_done(rdev, false);
36e6fea84   Johannes Berg   cfg80211: check f...
79
80
  	cfg80211_unlock_rdev(rdev);
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
81
82
  void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
  {
667503ddc   Johannes Berg   cfg80211: fix loc...
83
84
85
  	WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
  
  	request->aborted = aborted;
e60d7443e   Alban Browaeys   wireless : use a ...
86
  	queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk);
667503ddc   Johannes Berg   cfg80211: fix loc...
87
  }
2a5193119   Johannes Berg   cfg80211/nl80211:...
88
  EXPORT_SYMBOL(cfg80211_scan_done);
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
89
90
91
92
93
94
  void __cfg80211_sched_scan_results(struct work_struct *wk)
  {
  	struct cfg80211_registered_device *rdev;
  
  	rdev = container_of(wk, struct cfg80211_registered_device,
  			    sched_scan_results_wk);
c10841ca7   Luciano Coelho   cfg80211: fix dea...
95
  	mutex_lock(&rdev->sched_scan_mtx);
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
96
97
98
99
100
  
  	/* we don't have sched_scan_req anymore if the scan is stopping */
  	if (rdev->sched_scan_req)
  		nl80211_send_sched_scan_results(rdev,
  						rdev->sched_scan_req->dev);
c10841ca7   Luciano Coelho   cfg80211: fix dea...
101
  	mutex_unlock(&rdev->sched_scan_mtx);
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
102
103
104
105
106
107
108
109
110
111
  }
  
  void cfg80211_sched_scan_results(struct wiphy *wiphy)
  {
  	/* ignore if we're not scanning */
  	if (wiphy_to_dev(wiphy)->sched_scan_req)
  		queue_work(cfg80211_wq,
  			   &wiphy_to_dev(wiphy)->sched_scan_results_wk);
  }
  EXPORT_SYMBOL(cfg80211_sched_scan_results);
85a9994a0   Luciano Coelho   cfg80211/mac80211...
112
  void cfg80211_sched_scan_stopped(struct wiphy *wiphy)
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
113
  {
85a9994a0   Luciano Coelho   cfg80211/mac80211...
114
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
115

c10841ca7   Luciano Coelho   cfg80211: fix dea...
116
  	mutex_lock(&rdev->sched_scan_mtx);
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
117
  	__cfg80211_stop_sched_scan(rdev, true);
c10841ca7   Luciano Coelho   cfg80211: fix dea...
118
  	mutex_unlock(&rdev->sched_scan_mtx);
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
119
  }
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
120
121
122
123
124
  EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
  
  int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
  			       bool driver_initiated)
  {
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
125
  	struct net_device *dev;
c10841ca7   Luciano Coelho   cfg80211: fix dea...
126
  	lockdep_assert_held(&rdev->sched_scan_mtx);
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
127
128
  
  	if (!rdev->sched_scan_req)
1a84ff756   Luciano Coelho   cfg80211: return ...
129
  		return -ENOENT;
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
130
131
  
  	dev = rdev->sched_scan_req->dev;
85a9994a0   Luciano Coelho   cfg80211/mac80211...
132
  	if (!driver_initiated) {
3b4670ffe   Jesper Juhl   net, wireless: Do...
133
  		int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev);
85a9994a0   Luciano Coelho   cfg80211/mac80211...
134
135
136
  		if (err)
  			return err;
  	}
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
137
138
139
140
141
  
  	nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED);
  
  	kfree(rdev->sched_scan_req);
  	rdev->sched_scan_req = NULL;
3b4670ffe   Jesper Juhl   net, wireless: Do...
142
  	return 0;
807f8a8c3   Luciano Coelho   cfg80211/nl80211:...
143
  }
2a5193119   Johannes Berg   cfg80211/nl80211:...
144
145
146
147
148
  static void bss_release(struct kref *ref)
  {
  	struct cfg80211_internal_bss *bss;
  
  	bss = container_of(ref, struct cfg80211_internal_bss, ref);
78c1c7e10   Johannes Berg   cfg80211: free_pr...
149
150
  	if (bss->pub.free_priv)
  		bss->pub.free_priv(&bss->pub);
cd1658f59   Johannes Berg   cfg80211: do not ...
151

34a6eddba   Jouni Malinen   cfg80211: Store I...
152
153
154
155
  	if (bss->beacon_ies_allocated)
  		kfree(bss->pub.beacon_ies);
  	if (bss->proberesp_ies_allocated)
  		kfree(bss->pub.proberesp_ies);
cd1658f59   Johannes Berg   cfg80211: do not ...
156

19957bb39   Johannes Berg   cfg80211: keep tr...
157
  	BUG_ON(atomic_read(&bss->hold));
2a5193119   Johannes Berg   cfg80211/nl80211:...
158
159
160
161
  	kfree(bss);
  }
  
  /* must hold dev->bss_lock! */
cb3a8eec0   Dan Williams   cfg80211: age sca...
162
163
164
165
166
167
168
169
170
171
172
173
  void cfg80211_bss_age(struct cfg80211_registered_device *dev,
                        unsigned long age_secs)
  {
  	struct cfg80211_internal_bss *bss;
  	unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
  
  	list_for_each_entry(bss, &dev->bss_list, list) {
  		bss->ts -= age_jiffies;
  	}
  }
  
  /* must hold dev->bss_lock! */
2b78ac9bf   Juuso Oikarinen   cfg80211: fix BSS...
174
175
176
177
178
179
180
181
182
  static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
  				  struct cfg80211_internal_bss *bss)
  {
  	list_del_init(&bss->list);
  	rb_erase(&bss->rbn, &dev->bss_tree);
  	kref_put(&bss->ref, bss_release);
  }
  
  /* must hold dev->bss_lock! */
2a5193119   Johannes Berg   cfg80211/nl80211:...
183
184
185
186
187
188
  void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
  {
  	struct cfg80211_internal_bss *bss, *tmp;
  	bool expired = false;
  
  	list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
19957bb39   Johannes Berg   cfg80211: keep tr...
189
190
191
  		if (atomic_read(&bss->hold))
  			continue;
  		if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
2a5193119   Johannes Berg   cfg80211/nl80211:...
192
  			continue;
2b78ac9bf   Juuso Oikarinen   cfg80211: fix BSS...
193
  		__cfg80211_unlink_bss(dev, bss);
2a5193119   Johannes Berg   cfg80211/nl80211:...
194
195
196
197
198
199
  		expired = true;
  	}
  
  	if (expired)
  		dev->bss_generation++;
  }
c21dbf921   Johannes Berg   cfg80211: export ...
200
  const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
2a5193119   Johannes Berg   cfg80211/nl80211:...
201
  {
c21dbf921   Johannes Berg   cfg80211: export ...
202
  	while (len > 2 && ies[0] != eid) {
2a5193119   Johannes Berg   cfg80211/nl80211:...
203
204
205
206
207
208
209
210
211
  		len -= ies[1] + 2;
  		ies += ies[1] + 2;
  	}
  	if (len < 2)
  		return NULL;
  	if (len < 2 + ies[1])
  		return NULL;
  	return ies;
  }
c21dbf921   Johannes Berg   cfg80211: export ...
212
  EXPORT_SYMBOL(cfg80211_find_ie);
2a5193119   Johannes Berg   cfg80211/nl80211:...
213

0c28ec587   Eliad Peller   cfg80211: add cfg...
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
  const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
  				  const u8 *ies, int len)
  {
  	struct ieee80211_vendor_ie *ie;
  	const u8 *pos = ies, *end = ies + len;
  	int ie_oui;
  
  	while (pos < end) {
  		pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, pos,
  				       end - pos);
  		if (!pos)
  			return NULL;
  
  		if (end - pos < sizeof(*ie))
  			return NULL;
  
  		ie = (struct ieee80211_vendor_ie *)pos;
  		ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2];
  		if (ie_oui == oui && ie->oui_type == oui_type)
  			return pos;
  
  		pos += 2 + ie->len;
  	}
  	return NULL;
  }
  EXPORT_SYMBOL(cfg80211_find_vendor_ie);
2a5193119   Johannes Berg   cfg80211/nl80211:...
240
241
  static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
  {
c21dbf921   Johannes Berg   cfg80211: export ...
242
243
  	const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
  	const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
2a5193119   Johannes Berg   cfg80211/nl80211:...
244

3b6ef6334   Johannes Berg   cfg80211: fix cmp...
245
  	/* equal if both missing */
2a5193119   Johannes Berg   cfg80211/nl80211:...
246
247
  	if (!ie1 && !ie2)
  		return 0;
3b6ef6334   Johannes Berg   cfg80211: fix cmp...
248
249
  	/* sort missing IE before (left of) present IE */
  	if (!ie1)
2a5193119   Johannes Berg   cfg80211/nl80211:...
250
  		return -1;
3b6ef6334   Johannes Berg   cfg80211: fix cmp...
251
252
  	if (!ie2)
  		return 1;
2a5193119   Johannes Berg   cfg80211/nl80211:...
253

3b6ef6334   Johannes Berg   cfg80211: fix cmp...
254
255
  	/* sort by length first, then by contents */
  	if (ie1[1] != ie2[1])
2a5193119   Johannes Berg   cfg80211/nl80211:...
256
  		return ie2[1] - ie1[1];
3b6ef6334   Johannes Berg   cfg80211: fix cmp...
257
  	return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
2a5193119   Johannes Berg   cfg80211/nl80211:...
258
259
260
261
262
263
264
  }
  
  static bool is_bss(struct cfg80211_bss *a,
  		   const u8 *bssid,
  		   const u8 *ssid, size_t ssid_len)
  {
  	const u8 *ssidie;
79420f09e   Johannes Berg   cfg80211: add mor...
265
  	if (bssid && compare_ether_addr(a->bssid, bssid))
2a5193119   Johannes Berg   cfg80211/nl80211:...
266
  		return false;
79420f09e   Johannes Berg   cfg80211: add mor...
267
268
  	if (!ssid)
  		return true;
c21dbf921   Johannes Berg   cfg80211: export ...
269
270
271
  	ssidie = cfg80211_find_ie(WLAN_EID_SSID,
  				  a->information_elements,
  				  a->len_information_elements);
2a5193119   Johannes Berg   cfg80211/nl80211:...
272
273
274
275
276
277
  	if (!ssidie)
  		return false;
  	if (ssidie[1] != ssid_len)
  		return false;
  	return memcmp(ssidie + 2, ssid, ssid_len) == 0;
  }
333ba7325   Eliad Peller   cfg80211: don't d...
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
  static bool is_mesh_bss(struct cfg80211_bss *a)
  {
  	const u8 *ie;
  
  	if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
  		return false;
  
  	ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
  			      a->information_elements,
  			      a->len_information_elements);
  	if (!ie)
  		return false;
  
  	ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
  			      a->information_elements,
  			      a->len_information_elements);
  	if (!ie)
  		return false;
  
  	return true;
  }
2a5193119   Johannes Berg   cfg80211/nl80211:...
299
300
301
302
303
  static bool is_mesh(struct cfg80211_bss *a,
  		    const u8 *meshid, size_t meshidlen,
  		    const u8 *meshcfg)
  {
  	const u8 *ie;
333ba7325   Eliad Peller   cfg80211: don't d...
304
  	if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
2a5193119   Johannes Berg   cfg80211/nl80211:...
305
  		return false;
c21dbf921   Johannes Berg   cfg80211: export ...
306
307
308
  	ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
  			      a->information_elements,
  			      a->len_information_elements);
2a5193119   Johannes Berg   cfg80211/nl80211:...
309
310
311
312
313
314
  	if (!ie)
  		return false;
  	if (ie[1] != meshidlen)
  		return false;
  	if (memcmp(ie + 2, meshid, meshidlen))
  		return false;
c21dbf921   Johannes Berg   cfg80211: export ...
315
316
317
  	ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
  			      a->information_elements,
  			      a->len_information_elements);
cd3468bad   Johannes Berg   cfg80211: add two...
318
319
  	if (!ie)
  		return false;
136cfa286   Rui Paulo   mac80211: use a s...
320
  	if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
2a5193119   Johannes Berg   cfg80211/nl80211:...
321
322
323
324
325
326
327
  		return false;
  
  	/*
  	 * Ignore mesh capability (last two bytes of the IE) when
  	 * comparing since that may differ between stations taking
  	 * part in the same mesh.
  	 */
136cfa286   Rui Paulo   mac80211: use a s...
328
329
  	return memcmp(ie + 2, meshcfg,
  	    sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
2a5193119   Johannes Berg   cfg80211/nl80211:...
330
  }
dd9dfb9f9   Dmitry Tarnyagin   cfg80211: merge i...
331
332
  static int cmp_bss_core(struct cfg80211_bss *a,
  			struct cfg80211_bss *b)
2a5193119   Johannes Berg   cfg80211/nl80211:...
333
334
335
336
337
  {
  	int r;
  
  	if (a->channel != b->channel)
  		return b->channel->center_freq - a->channel->center_freq;
333ba7325   Eliad Peller   cfg80211: don't d...
338
  	if (is_mesh_bss(a) && is_mesh_bss(b)) {
2a5193119   Johannes Berg   cfg80211/nl80211:...
339
340
341
342
343
344
345
346
347
348
349
350
351
  		r = cmp_ies(WLAN_EID_MESH_ID,
  			    a->information_elements,
  			    a->len_information_elements,
  			    b->information_elements,
  			    b->len_information_elements);
  		if (r)
  			return r;
  		return cmp_ies(WLAN_EID_MESH_CONFIG,
  			       a->information_elements,
  			       a->len_information_elements,
  			       b->information_elements,
  			       b->len_information_elements);
  	}
dd9dfb9f9   Dmitry Tarnyagin   cfg80211: merge i...
352
353
354
355
356
357
358
359
360
  	return memcmp(a->bssid, b->bssid, ETH_ALEN);
  }
  
  static int cmp_bss(struct cfg80211_bss *a,
  		   struct cfg80211_bss *b)
  {
  	int r;
  
  	r = cmp_bss_core(a, b);
0a35d36d6   Javier Cardona   cfg80211: Use cap...
361
362
  	if (r)
  		return r;
2a5193119   Johannes Berg   cfg80211/nl80211:...
363
364
365
366
367
368
  	return cmp_ies(WLAN_EID_SSID,
  		       a->information_elements,
  		       a->len_information_elements,
  		       b->information_elements,
  		       b->len_information_elements);
  }
dd9dfb9f9   Dmitry Tarnyagin   cfg80211: merge i...
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  static int cmp_hidden_bss(struct cfg80211_bss *a,
  		   struct cfg80211_bss *b)
  {
  	const u8 *ie1;
  	const u8 *ie2;
  	int i;
  	int r;
  
  	r = cmp_bss_core(a, b);
  	if (r)
  		return r;
  
  	ie1 = cfg80211_find_ie(WLAN_EID_SSID,
  			a->information_elements,
  			a->len_information_elements);
  	ie2 = cfg80211_find_ie(WLAN_EID_SSID,
  			b->information_elements,
  			b->len_information_elements);
  
  	/* Key comparator must use same algorithm in any rb-tree
  	 * search function (order is important), otherwise ordering
  	 * of items in the tree is broken and search gives incorrect
  	 * results. This code uses same order as cmp_ies() does. */
  
  	/* sort missing IE before (left of) present IE */
  	if (!ie1)
  		return -1;
  	if (!ie2)
  		return 1;
  
  	/* zero-size SSID is used as an indication of the hidden bss */
  	if (!ie2[1])
  		return 0;
  
  	/* sort by length first, then by contents */
  	if (ie1[1] != ie2[1])
  		return ie2[1] - ie1[1];
  
  	/* zeroed SSID ie is another indication of a hidden bss */
  	for (i = 0; i < ie2[1]; i++)
  		if (ie2[i + 2])
  			return -1;
  
  	return 0;
  }
2a5193119   Johannes Berg   cfg80211/nl80211:...
414
415
416
  struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
  				      struct ieee80211_channel *channel,
  				      const u8 *bssid,
79420f09e   Johannes Berg   cfg80211: add mor...
417
418
  				      const u8 *ssid, size_t ssid_len,
  				      u16 capa_mask, u16 capa_val)
2a5193119   Johannes Berg   cfg80211/nl80211:...
419
420
421
  {
  	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
  	struct cfg80211_internal_bss *bss, *res = NULL;
ccb6c1360   Johannes Berg   cfg80211: don't g...
422
  	unsigned long now = jiffies;
2a5193119   Johannes Berg   cfg80211/nl80211:...
423
424
425
426
  
  	spin_lock_bh(&dev->bss_lock);
  
  	list_for_each_entry(bss, &dev->bss_list, list) {
79420f09e   Johannes Berg   cfg80211: add mor...
427
428
  		if ((bss->pub.capability & capa_mask) != capa_val)
  			continue;
2a5193119   Johannes Berg   cfg80211/nl80211:...
429
430
  		if (channel && bss->pub.channel != channel)
  			continue;
ccb6c1360   Johannes Berg   cfg80211: don't g...
431
432
433
434
  		/* Don't get expired BSS structs */
  		if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
  		    !atomic_read(&bss->hold))
  			continue;
2a5193119   Johannes Berg   cfg80211/nl80211:...
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
  		if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
  			res = bss;
  			kref_get(&res->ref);
  			break;
  		}
  	}
  
  	spin_unlock_bh(&dev->bss_lock);
  	if (!res)
  		return NULL;
  	return &res->pub;
  }
  EXPORT_SYMBOL(cfg80211_get_bss);
  
  struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy,
  				       struct ieee80211_channel *channel,
  				       const u8 *meshid, size_t meshidlen,
  				       const u8 *meshcfg)
  {
  	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
  	struct cfg80211_internal_bss *bss, *res = NULL;
  
  	spin_lock_bh(&dev->bss_lock);
  
  	list_for_each_entry(bss, &dev->bss_list, list) {
  		if (channel && bss->pub.channel != channel)
  			continue;
  		if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) {
  			res = bss;
  			kref_get(&res->ref);
  			break;
  		}
  	}
  
  	spin_unlock_bh(&dev->bss_lock);
  	if (!res)
  		return NULL;
  	return &res->pub;
  }
  EXPORT_SYMBOL(cfg80211_get_mesh);
  
  
  static void rb_insert_bss(struct cfg80211_registered_device *dev,
  			  struct cfg80211_internal_bss *bss)
  {
  	struct rb_node **p = &dev->bss_tree.rb_node;
  	struct rb_node *parent = NULL;
  	struct cfg80211_internal_bss *tbss;
  	int cmp;
  
  	while (*p) {
  		parent = *p;
  		tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
  
  		cmp = cmp_bss(&bss->pub, &tbss->pub);
  
  		if (WARN_ON(!cmp)) {
  			/* will sort of leak this BSS */
  			return;
  		}
  
  		if (cmp < 0)
  			p = &(*p)->rb_left;
  		else
  			p = &(*p)->rb_right;
  	}
  
  	rb_link_node(&bss->rbn, parent, p);
  	rb_insert_color(&bss->rbn, &dev->bss_tree);
  }
  
  static struct cfg80211_internal_bss *
  rb_find_bss(struct cfg80211_registered_device *dev,
  	    struct cfg80211_internal_bss *res)
  {
  	struct rb_node *n = dev->bss_tree.rb_node;
  	struct cfg80211_internal_bss *bss;
  	int r;
  
  	while (n) {
  		bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
  		r = cmp_bss(&res->pub, &bss->pub);
  
  		if (r == 0)
  			return bss;
  		else if (r < 0)
  			n = n->rb_left;
  		else
  			n = n->rb_right;
  	}
  
  	return NULL;
  }
  
  static struct cfg80211_internal_bss *
dd9dfb9f9   Dmitry Tarnyagin   cfg80211: merge i...
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
  rb_find_hidden_bss(struct cfg80211_registered_device *dev,
  	    struct cfg80211_internal_bss *res)
  {
  	struct rb_node *n = dev->bss_tree.rb_node;
  	struct cfg80211_internal_bss *bss;
  	int r;
  
  	while (n) {
  		bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
  		r = cmp_hidden_bss(&res->pub, &bss->pub);
  
  		if (r == 0)
  			return bss;
  		else if (r < 0)
  			n = n->rb_left;
  		else
  			n = n->rb_right;
  	}
  
  	return NULL;
  }
  
  static void
  copy_hidden_ies(struct cfg80211_internal_bss *res,
  		 struct cfg80211_internal_bss *hidden)
  {
  	if (unlikely(res->pub.beacon_ies))
  		return;
  	if (WARN_ON(!hidden->pub.beacon_ies))
  		return;
  
  	res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC);
  	if (unlikely(!res->pub.beacon_ies))
  		return;
  
  	res->beacon_ies_allocated = true;
  	res->pub.len_beacon_ies = hidden->pub.len_beacon_ies;
  	memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies,
  			res->pub.len_beacon_ies);
  }
  
  static struct cfg80211_internal_bss *
2a5193119   Johannes Berg   cfg80211/nl80211:...
572
  cfg80211_bss_update(struct cfg80211_registered_device *dev,
34a6eddba   Jouni Malinen   cfg80211: Store I...
573
  		    struct cfg80211_internal_bss *res)
2a5193119   Johannes Berg   cfg80211/nl80211:...
574
575
  {
  	struct cfg80211_internal_bss *found = NULL;
2a5193119   Johannes Berg   cfg80211/nl80211:...
576
577
578
579
580
581
582
583
584
585
586
  
  	/*
  	 * The reference to "res" is donated to this function.
  	 */
  
  	if (WARN_ON(!res->pub.channel)) {
  		kref_put(&res->ref, bss_release);
  		return NULL;
  	}
  
  	res->ts = jiffies;
2a5193119   Johannes Berg   cfg80211/nl80211:...
587
588
589
  	spin_lock_bh(&dev->bss_lock);
  
  	found = rb_find_bss(dev, res);
cd1658f59   Johannes Berg   cfg80211: do not ...
590
  	if (found) {
2a5193119   Johannes Berg   cfg80211/nl80211:...
591
592
593
  		found->pub.beacon_interval = res->pub.beacon_interval;
  		found->pub.tsf = res->pub.tsf;
  		found->pub.signal = res->pub.signal;
2a5193119   Johannes Berg   cfg80211/nl80211:...
594
595
  		found->pub.capability = res->pub.capability;
  		found->ts = res->ts;
cd1658f59   Johannes Berg   cfg80211: do not ...
596

34a6eddba   Jouni Malinen   cfg80211: Store I...
597
598
  		/* Update IEs */
  		if (res->pub.proberesp_ies) {
cd1658f59   Johannes Berg   cfg80211: do not ...
599
  			size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
34a6eddba   Jouni Malinen   cfg80211: Store I...
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
  			size_t ielen = res->pub.len_proberesp_ies;
  
  			if (found->pub.proberesp_ies &&
  			    !found->proberesp_ies_allocated &&
  			    ksize(found) >= used + ielen) {
  				memcpy(found->pub.proberesp_ies,
  				       res->pub.proberesp_ies, ielen);
  				found->pub.len_proberesp_ies = ielen;
  			} else {
  				u8 *ies = found->pub.proberesp_ies;
  
  				if (found->proberesp_ies_allocated)
  					ies = krealloc(ies, ielen, GFP_ATOMIC);
  				else
  					ies = kmalloc(ielen, GFP_ATOMIC);
  
  				if (ies) {
  					memcpy(ies, res->pub.proberesp_ies,
  					       ielen);
  					found->proberesp_ies_allocated = true;
  					found->pub.proberesp_ies = ies;
  					found->pub.len_proberesp_ies = ielen;
  				}
  			}
cd1658f59   Johannes Berg   cfg80211: do not ...
624

34a6eddba   Jouni Malinen   cfg80211: Store I...
625
626
627
628
629
630
631
632
633
  			/* Override possible earlier Beacon frame IEs */
  			found->pub.information_elements =
  				found->pub.proberesp_ies;
  			found->pub.len_information_elements =
  				found->pub.len_proberesp_ies;
  		}
  		if (res->pub.beacon_ies) {
  			size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
  			size_t ielen = res->pub.len_beacon_ies;
01123e233   Sven Neumann   cfg80211: update ...
634
635
636
  			bool information_elements_is_beacon_ies =
  				(found->pub.information_elements ==
  				 found->pub.beacon_ies);
34a6eddba   Jouni Malinen   cfg80211: Store I...
637
638
639
640
641
642
643
  
  			if (found->pub.beacon_ies &&
  			    !found->beacon_ies_allocated &&
  			    ksize(found) >= used + ielen) {
  				memcpy(found->pub.beacon_ies,
  				       res->pub.beacon_ies, ielen);
  				found->pub.len_beacon_ies = ielen;
cd1658f59   Johannes Berg   cfg80211: do not ...
644
  			} else {
34a6eddba   Jouni Malinen   cfg80211: Store I...
645
  				u8 *ies = found->pub.beacon_ies;
cd1658f59   Johannes Berg   cfg80211: do not ...
646

34a6eddba   Jouni Malinen   cfg80211: Store I...
647
  				if (found->beacon_ies_allocated)
273de92c8   Michael Buesch   cfg80211: Remove ...
648
649
  					ies = krealloc(ies, ielen, GFP_ATOMIC);
  				else
cd1658f59   Johannes Berg   cfg80211: do not ...
650
651
652
  					ies = kmalloc(ielen, GFP_ATOMIC);
  
  				if (ies) {
34a6eddba   Jouni Malinen   cfg80211: Store I...
653
654
655
656
657
  					memcpy(ies, res->pub.beacon_ies,
  					       ielen);
  					found->beacon_ies_allocated = true;
  					found->pub.beacon_ies = ies;
  					found->pub.len_beacon_ies = ielen;
cd1658f59   Johannes Berg   cfg80211: do not ...
658
659
  				}
  			}
01123e233   Sven Neumann   cfg80211: update ...
660
661
662
663
664
665
666
667
  
  			/* Override IEs if they were from a beacon before */
  			if (information_elements_is_beacon_ies) {
  				found->pub.information_elements =
  					found->pub.beacon_ies;
  				found->pub.len_information_elements =
  					found->pub.len_beacon_ies;
  			}
cd1658f59   Johannes Berg   cfg80211: do not ...
668
  		}
2a5193119   Johannes Berg   cfg80211/nl80211:...
669
670
  		kref_put(&res->ref, bss_release);
  	} else {
dd9dfb9f9   Dmitry Tarnyagin   cfg80211: merge i...
671
672
673
674
675
676
677
678
679
680
681
682
683
684
  		struct cfg80211_internal_bss *hidden;
  
  		/* First check if the beacon is a probe response from
  		 * a hidden bss. If so, copy beacon ies (with nullified
  		 * ssid) into the probe response bss entry (with real ssid).
  		 * It is required basically for PSM implementation
  		 * (probe responses do not contain tim ie) */
  
  		/* TODO: The code is not trying to update existing probe
  		 * response bss entries when beacon ies are
  		 * getting changed. */
  		hidden = rb_find_hidden_bss(dev, res);
  		if (hidden)
  			copy_hidden_ies(res, hidden);
2a5193119   Johannes Berg   cfg80211/nl80211:...
685
686
687
688
689
690
691
692
693
694
695
696
  		/* this "consumes" the reference */
  		list_add_tail(&res->list, &dev->bss_list);
  		rb_insert_bss(dev, res);
  		found = res;
  	}
  
  	dev->bss_generation++;
  	spin_unlock_bh(&dev->bss_lock);
  
  	kref_get(&found->ref);
  	return found;
  }
06aa7afaa   Jussi Kivilinna   cfg80211: add cfg...
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
  struct cfg80211_bss*
  cfg80211_inform_bss(struct wiphy *wiphy,
  		    struct ieee80211_channel *channel,
  		    const u8 *bssid,
  		    u64 timestamp, u16 capability, u16 beacon_interval,
  		    const u8 *ie, size_t ielen,
  		    s32 signal, gfp_t gfp)
  {
  	struct cfg80211_internal_bss *res;
  	size_t privsz;
  
  	if (WARN_ON(!wiphy))
  		return NULL;
  
  	privsz = wiphy->bss_priv_size;
22fe88d3d   Sujith   cfg80211: Fix sig...
712
  	if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
06aa7afaa   Jussi Kivilinna   cfg80211: add cfg...
713
714
715
716
717
718
719
720
721
722
723
724
725
  			(signal < 0 || signal > 100)))
  		return NULL;
  
  	res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
  	if (!res)
  		return NULL;
  
  	memcpy(res->pub.bssid, bssid, ETH_ALEN);
  	res->pub.channel = channel;
  	res->pub.signal = signal;
  	res->pub.tsf = timestamp;
  	res->pub.beacon_interval = beacon_interval;
  	res->pub.capability = capability;
34a6eddba   Jouni Malinen   cfg80211: Store I...
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
  	/*
  	 * Since we do not know here whether the IEs are from a Beacon or Probe
  	 * Response frame, we need to pick one of the options and only use it
  	 * with the driver that does not provide the full Beacon/Probe Response
  	 * frame. Use Beacon frame pointer to avoid indicating that this should
  	 * override the information_elements pointer should we have received an
  	 * earlier indication of Probe Response data.
  	 *
  	 * The initial buffer for the IEs is allocated with the BSS entry and
  	 * is located after the private area.
  	 */
  	res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz;
  	memcpy(res->pub.beacon_ies, ie, ielen);
  	res->pub.len_beacon_ies = ielen;
  	res->pub.information_elements = res->pub.beacon_ies;
  	res->pub.len_information_elements = res->pub.len_beacon_ies;
06aa7afaa   Jussi Kivilinna   cfg80211: add cfg...
742
743
  
  	kref_init(&res->ref);
34a6eddba   Jouni Malinen   cfg80211: Store I...
744
  	res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
06aa7afaa   Jussi Kivilinna   cfg80211: add cfg...
745
746
747
748
749
750
751
752
753
754
  	if (!res)
  		return NULL;
  
  	if (res->pub.capability & WLAN_CAPABILITY_ESS)
  		regulatory_hint_found_beacon(wiphy, channel, gfp);
  
  	/* cfg80211_bss_update gives us a referenced result */
  	return &res->pub;
  }
  EXPORT_SYMBOL(cfg80211_inform_bss);
2a5193119   Johannes Berg   cfg80211/nl80211:...
755
756
757
758
  struct cfg80211_bss *
  cfg80211_inform_bss_frame(struct wiphy *wiphy,
  			  struct ieee80211_channel *channel,
  			  struct ieee80211_mgmt *mgmt, size_t len,
77965c970   Johannes Berg   cfg80211: clean u...
759
  			  s32 signal, gfp_t gfp)
2a5193119   Johannes Berg   cfg80211/nl80211:...
760
761
762
763
  {
  	struct cfg80211_internal_bss *res;
  	size_t ielen = len - offsetof(struct ieee80211_mgmt,
  				      u.probe_resp.variable);
bef9bacc4   Mariusz Kozlowski   cfg80211:: fix po...
764
765
766
767
768
769
770
  	size_t privsz;
  
  	if (WARN_ON(!mgmt))
  		return NULL;
  
  	if (WARN_ON(!wiphy))
  		return NULL;
2a5193119   Johannes Berg   cfg80211/nl80211:...
771

22fe88d3d   Sujith   cfg80211: Fix sig...
772
  	if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
2a5193119   Johannes Berg   cfg80211/nl80211:...
773
774
  	            (signal < 0 || signal > 100)))
  		return NULL;
bef9bacc4   Mariusz Kozlowski   cfg80211:: fix po...
775
  	if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
2a5193119   Johannes Berg   cfg80211/nl80211:...
776
  		return NULL;
bef9bacc4   Mariusz Kozlowski   cfg80211:: fix po...
777
  	privsz = wiphy->bss_priv_size;
2a5193119   Johannes Berg   cfg80211/nl80211:...
778
779
780
781
782
783
  	res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
  	if (!res)
  		return NULL;
  
  	memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN);
  	res->pub.channel = channel;
2a5193119   Johannes Berg   cfg80211/nl80211:...
784
785
786
787
  	res->pub.signal = signal;
  	res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
  	res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
  	res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
34a6eddba   Jouni Malinen   cfg80211: Store I...
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
  	/*
  	 * The initial buffer for the IEs is allocated with the BSS entry and
  	 * is located after the private area.
  	 */
  	if (ieee80211_is_probe_resp(mgmt->frame_control)) {
  		res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz;
  		memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable,
  		       ielen);
  		res->pub.len_proberesp_ies = ielen;
  		res->pub.information_elements = res->pub.proberesp_ies;
  		res->pub.len_information_elements = res->pub.len_proberesp_ies;
  	} else {
  		res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz;
  		memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen);
  		res->pub.len_beacon_ies = ielen;
  		res->pub.information_elements = res->pub.beacon_ies;
  		res->pub.len_information_elements = res->pub.len_beacon_ies;
  	}
2a5193119   Johannes Berg   cfg80211/nl80211:...
806
807
  
  	kref_init(&res->ref);
34a6eddba   Jouni Malinen   cfg80211: Store I...
808
  	res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
2a5193119   Johannes Berg   cfg80211/nl80211:...
809
810
  	if (!res)
  		return NULL;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
811
812
  	if (res->pub.capability & WLAN_CAPABILITY_ESS)
  		regulatory_hint_found_beacon(wiphy, channel, gfp);
2a5193119   Johannes Berg   cfg80211/nl80211:...
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
  	/* cfg80211_bss_update gives us a referenced result */
  	return &res->pub;
  }
  EXPORT_SYMBOL(cfg80211_inform_bss_frame);
  
  void cfg80211_put_bss(struct cfg80211_bss *pub)
  {
  	struct cfg80211_internal_bss *bss;
  
  	if (!pub)
  		return;
  
  	bss = container_of(pub, struct cfg80211_internal_bss, pub);
  	kref_put(&bss->ref, bss_release);
  }
  EXPORT_SYMBOL(cfg80211_put_bss);
d491af19d   Johannes Berg   cfg80211: allow u...
829
830
831
832
833
834
835
836
837
838
839
  void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
  {
  	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
  	struct cfg80211_internal_bss *bss;
  
  	if (WARN_ON(!pub))
  		return;
  
  	bss = container_of(pub, struct cfg80211_internal_bss, pub);
  
  	spin_lock_bh(&dev->bss_lock);
3207390a8   Johannes Berg   cfg80211: fix BSS...
840
  	if (!list_empty(&bss->list)) {
2b78ac9bf   Juuso Oikarinen   cfg80211: fix BSS...
841
  		__cfg80211_unlink_bss(dev, bss);
3207390a8   Johannes Berg   cfg80211: fix BSS...
842
  		dev->bss_generation++;
3207390a8   Johannes Berg   cfg80211: fix BSS...
843
  	}
d491af19d   Johannes Berg   cfg80211: allow u...
844
  	spin_unlock_bh(&dev->bss_lock);
d491af19d   Johannes Berg   cfg80211: allow u...
845
846
  }
  EXPORT_SYMBOL(cfg80211_unlink_bss);
3d23e349d   Johannes Berg   wext: refactor
847
  #ifdef CONFIG_CFG80211_WEXT
2a5193119   Johannes Berg   cfg80211/nl80211:...
848
849
850
851
852
853
854
  int cfg80211_wext_siwscan(struct net_device *dev,
  			  struct iw_request_info *info,
  			  union iwreq_data *wrqu, char *extra)
  {
  	struct cfg80211_registered_device *rdev;
  	struct wiphy *wiphy;
  	struct iw_scan_req *wreq = NULL;
65486c8b3   Johannes Berg   cfg80211: fix err...
855
  	struct cfg80211_scan_request *creq = NULL;
2a5193119   Johannes Berg   cfg80211/nl80211:...
856
857
858
859
860
  	int i, err, n_channels = 0;
  	enum ieee80211_band band;
  
  	if (!netif_running(dev))
  		return -ENETDOWN;
b2e3abdc7   Holger Schurig   cfg80211: allow s...
861
862
  	if (wrqu->data.length == sizeof(struct iw_scan_req))
  		wreq = (struct iw_scan_req *)extra;
463d01832   Johannes Berg   cfg80211: make aw...
863
  	rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
2a5193119   Johannes Berg   cfg80211/nl80211:...
864
865
866
867
868
869
870
871
872
873
  
  	if (IS_ERR(rdev))
  		return PTR_ERR(rdev);
  
  	if (rdev->scan_req) {
  		err = -EBUSY;
  		goto out;
  	}
  
  	wiphy = &rdev->wiphy;
b2e3abdc7   Holger Schurig   cfg80211: allow s...
874
875
876
877
878
879
880
881
  	/* Determine number of channels, needed to allocate creq */
  	if (wreq && wreq->num_channels)
  		n_channels = wreq->num_channels;
  	else {
  		for (band = 0; band < IEEE80211_NUM_BANDS; band++)
  			if (wiphy->bands[band])
  				n_channels += wiphy->bands[band]->n_channels;
  	}
2a5193119   Johannes Berg   cfg80211/nl80211:...
882
883
884
885
886
887
888
889
890
891
  
  	creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
  		       n_channels * sizeof(void *),
  		       GFP_ATOMIC);
  	if (!creq) {
  		err = -ENOMEM;
  		goto out;
  	}
  
  	creq->wiphy = wiphy;
463d01832   Johannes Berg   cfg80211: make aw...
892
  	creq->dev = dev;
5ba63533b   Johannes Berg   cfg80211: fix ali...
893
894
  	/* SSIDs come after channels */
  	creq->ssids = (void *)&creq->channels[n_channels];
2a5193119   Johannes Berg   cfg80211/nl80211:...
895
896
  	creq->n_channels = n_channels;
  	creq->n_ssids = 1;
b2e3abdc7   Holger Schurig   cfg80211: allow s...
897
  	/* translate "Scan on frequencies" request */
2a5193119   Johannes Berg   cfg80211/nl80211:...
898
899
900
  	i = 0;
  	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
  		int j;
584991dcc   Johannes Berg   cfg80211: validat...
901

2a5193119   Johannes Berg   cfg80211/nl80211:...
902
903
  		if (!wiphy->bands[band])
  			continue;
584991dcc   Johannes Berg   cfg80211: validat...
904

2a5193119   Johannes Berg   cfg80211/nl80211:...
905
  		for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
584991dcc   Johannes Berg   cfg80211: validat...
906
907
908
909
  			/* ignore disabled channels */
  			if (wiphy->bands[band]->channels[j].flags &
  						IEEE80211_CHAN_DISABLED)
  				continue;
b2e3abdc7   Holger Schurig   cfg80211: allow s...
910
911
912
913
914
915
916
917
918
  
  			/* If we have a wireless request structure and the
  			 * wireless request specifies frequencies, then search
  			 * for the matching hardware channel.
  			 */
  			if (wreq && wreq->num_channels) {
  				int k;
  				int wiphy_freq = wiphy->bands[band]->channels[j].center_freq;
  				for (k = 0; k < wreq->num_channels; k++) {
a4e7b730f   Holger Schurig   cfg80211: use cfg...
919
  					int wext_freq = cfg80211_wext_freq(wiphy, &wreq->channel_list[k]);
b2e3abdc7   Holger Schurig   cfg80211: allow s...
920
921
922
923
924
925
926
  					if (wext_freq == wiphy_freq)
  						goto wext_freq_found;
  				}
  				goto wext_freq_not_found;
  			}
  
  		wext_freq_found:
2a5193119   Johannes Berg   cfg80211/nl80211:...
927
928
  			creq->channels[i] = &wiphy->bands[band]->channels[j];
  			i++;
b2e3abdc7   Holger Schurig   cfg80211: allow s...
929
  		wext_freq_not_found: ;
2a5193119   Johannes Berg   cfg80211/nl80211:...
930
931
  		}
  	}
8862dc5f2   Holger Schurig   cfg80211: minimal...
932
933
934
935
936
  	/* No channels found? */
  	if (!i) {
  		err = -EINVAL;
  		goto out;
  	}
2a5193119   Johannes Berg   cfg80211/nl80211:...
937

b2e3abdc7   Holger Schurig   cfg80211: allow s...
938
939
  	/* Set real number of channels specified in creq->channels[] */
  	creq->n_channels = i;
2a5193119   Johannes Berg   cfg80211/nl80211:...
940

b2e3abdc7   Holger Schurig   cfg80211: allow s...
941
942
  	/* translate "Scan for SSID" request */
  	if (wreq) {
2a5193119   Johannes Berg   cfg80211/nl80211:...
943
  		if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
65486c8b3   Johannes Berg   cfg80211: fix err...
944
945
946
947
  			if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
  				err = -EINVAL;
  				goto out;
  			}
2a5193119   Johannes Berg   cfg80211/nl80211:...
948
949
950
951
952
953
  			memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
  			creq->ssids[0].ssid_len = wreq->essid_len;
  		}
  		if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
  			creq->n_ssids = 0;
  	}
34850ab25   Johannes Berg   cfg80211: allow u...
954
  	for (i = 0; i < IEEE80211_NUM_BANDS; i++)
a401d2bb3   Johannes Berg   cfg80211: fix sca...
955
956
  		if (wiphy->bands[i])
  			creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
34850ab25   Johannes Berg   cfg80211: allow u...
957

2a5193119   Johannes Berg   cfg80211/nl80211:...
958
959
960
961
  	rdev->scan_req = creq;
  	err = rdev->ops->scan(wiphy, dev, creq);
  	if (err) {
  		rdev->scan_req = NULL;
65486c8b3   Johannes Berg   cfg80211: fix err...
962
  		/* creq will be freed below */
463d01832   Johannes Berg   cfg80211: make aw...
963
  	} else {
a538e2d5a   Johannes Berg   cfg80211: issue n...
964
  		nl80211_send_scan_start(rdev, dev);
65486c8b3   Johannes Berg   cfg80211: fix err...
965
966
  		/* creq now owned by driver */
  		creq = NULL;
463d01832   Johannes Berg   cfg80211: make aw...
967
968
  		dev_hold(dev);
  	}
2a5193119   Johannes Berg   cfg80211/nl80211:...
969
   out:
65486c8b3   Johannes Berg   cfg80211: fix err...
970
  	kfree(creq);
4d0c8aead   Johannes Berg   cfg80211: properl...
971
  	cfg80211_unlock_rdev(rdev);
2a5193119   Johannes Berg   cfg80211/nl80211:...
972
973
  	return err;
  }
ba44cb722   Johannes Berg   cfg80211: mark al...
974
  EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
2a5193119   Johannes Berg   cfg80211/nl80211:...
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
  
  static void ieee80211_scan_add_ies(struct iw_request_info *info,
  				   struct cfg80211_bss *bss,
  				   char **current_ev, char *end_buf)
  {
  	u8 *pos, *end, *next;
  	struct iw_event iwe;
  
  	if (!bss->information_elements ||
  	    !bss->len_information_elements)
  		return;
  
  	/*
  	 * If needed, fragment the IEs buffer (at IE boundaries) into short
  	 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
  	 */
  	pos = bss->information_elements;
  	end = pos + bss->len_information_elements;
  
  	while (end - pos > IW_GENERIC_IE_MAX) {
  		next = pos + 2 + pos[1];
  		while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
  			next = next + 2 + next[1];
  
  		memset(&iwe, 0, sizeof(iwe));
  		iwe.cmd = IWEVGENIE;
  		iwe.u.data.length = next - pos;
  		*current_ev = iwe_stream_add_point(info, *current_ev,
  						   end_buf, &iwe, pos);
  
  		pos = next;
  	}
  
  	if (end > pos) {
  		memset(&iwe, 0, sizeof(iwe));
  		iwe.cmd = IWEVGENIE;
  		iwe.u.data.length = end - pos;
  		*current_ev = iwe_stream_add_point(info, *current_ev,
  						   end_buf, &iwe, pos);
  	}
  }
cb3a8eec0   Dan Williams   cfg80211: age sca...
1016
1017
1018
1019
1020
1021
1022
1023
1024
  static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
  {
  	unsigned long end = jiffies;
  
  	if (end >= start)
  		return jiffies_to_msecs(end - start);
  
  	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
  }
2a5193119   Johannes Berg   cfg80211/nl80211:...
1025
1026
  
  static char *
77965c970   Johannes Berg   cfg80211: clean u...
1027
1028
1029
  ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
  	      struct cfg80211_internal_bss *bss, char *current_ev,
  	      char *end_buf)
2a5193119   Johannes Berg   cfg80211/nl80211:...
1030
1031
1032
1033
  {
  	struct iw_event iwe;
  	u8 *buf, *cfg, *p;
  	u8 *ie = bss->pub.information_elements;
a77b85524   Johannes Berg   cfg80211/mac80211...
1034
  	int rem = bss->pub.len_information_elements, i, sig;
2a5193119   Johannes Berg   cfg80211/nl80211:...
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
  	bool ismesh = false;
  
  	memset(&iwe, 0, sizeof(iwe));
  	iwe.cmd = SIOCGIWAP;
  	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
  	memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
  	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
  					  IW_EV_ADDR_LEN);
  
  	memset(&iwe, 0, sizeof(iwe));
  	iwe.cmd = SIOCGIWFREQ;
  	iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
  	iwe.u.freq.e = 0;
  	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
  					  IW_EV_FREQ_LEN);
  
  	memset(&iwe, 0, sizeof(iwe));
  	iwe.cmd = SIOCGIWFREQ;
  	iwe.u.freq.m = bss->pub.channel->center_freq;
  	iwe.u.freq.e = 6;
  	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
  					  IW_EV_FREQ_LEN);
77965c970   Johannes Berg   cfg80211: clean u...
1057
  	if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
2a5193119   Johannes Berg   cfg80211/nl80211:...
1058
1059
1060
1061
  		memset(&iwe, 0, sizeof(iwe));
  		iwe.cmd = IWEVQUAL;
  		iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
  				     IW_QUAL_NOISE_INVALID |
a77b85524   Johannes Berg   cfg80211/mac80211...
1062
  				     IW_QUAL_QUAL_UPDATED;
77965c970   Johannes Berg   cfg80211: clean u...
1063
  		switch (wiphy->signal_type) {
2a5193119   Johannes Berg   cfg80211/nl80211:...
1064
  		case CFG80211_SIGNAL_TYPE_MBM:
a77b85524   Johannes Berg   cfg80211/mac80211...
1065
1066
  			sig = bss->pub.signal / 100;
  			iwe.u.qual.level = sig;
2a5193119   Johannes Berg   cfg80211/nl80211:...
1067
  			iwe.u.qual.updated |= IW_QUAL_DBM;
a77b85524   Johannes Berg   cfg80211/mac80211...
1068
1069
1070
1071
1072
1073
  			if (sig < -110)		/* rather bad */
  				sig = -110;
  			else if (sig > -40)	/* perfect */
  				sig = -40;
  			/* will give a range of 0 .. 70 */
  			iwe.u.qual.qual = sig + 110;
2a5193119   Johannes Berg   cfg80211/nl80211:...
1074
1075
1076
  			break;
  		case CFG80211_SIGNAL_TYPE_UNSPEC:
  			iwe.u.qual.level = bss->pub.signal;
a77b85524   Johannes Berg   cfg80211/mac80211...
1077
1078
  			/* will give range 0 .. 100 */
  			iwe.u.qual.qual = bss->pub.signal;
2a5193119   Johannes Berg   cfg80211/nl80211:...
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
  			break;
  		default:
  			/* not reached */
  			break;
  		}
  		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
  						  &iwe, IW_EV_QUAL_LEN);
  	}
  
  	memset(&iwe, 0, sizeof(iwe));
  	iwe.cmd = SIOCGIWENCODE;
  	if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
  		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
  	else
  		iwe.u.data.flags = IW_ENCODE_DISABLED;
  	iwe.u.data.length = 0;
  	current_ev = iwe_stream_add_point(info, current_ev, end_buf,
  					  &iwe, "");
  
  	while (rem >= 2) {
  		/* invalid data */
  		if (ie[1] > rem - 2)
  			break;
  
  		switch (ie[0]) {
  		case WLAN_EID_SSID:
  			memset(&iwe, 0, sizeof(iwe));
  			iwe.cmd = SIOCGIWESSID;
  			iwe.u.data.length = ie[1];
  			iwe.u.data.flags = 1;
  			current_ev = iwe_stream_add_point(info, current_ev, end_buf,
  							  &iwe, ie + 2);
  			break;
  		case WLAN_EID_MESH_ID:
  			memset(&iwe, 0, sizeof(iwe));
  			iwe.cmd = SIOCGIWESSID;
  			iwe.u.data.length = ie[1];
  			iwe.u.data.flags = 1;
  			current_ev = iwe_stream_add_point(info, current_ev, end_buf,
  							  &iwe, ie + 2);
  			break;
  		case WLAN_EID_MESH_CONFIG:
  			ismesh = true;
136cfa286   Rui Paulo   mac80211: use a s...
1122
  			if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
2a5193119   Johannes Berg   cfg80211/nl80211:...
1123
1124
1125
1126
1127
1128
1129
  				break;
  			buf = kmalloc(50, GFP_ATOMIC);
  			if (!buf)
  				break;
  			cfg = ie + 2;
  			memset(&iwe, 0, sizeof(iwe));
  			iwe.cmd = IWEVCUSTOM;
76aa5e704   Rui Paulo   mac80211: update ...
1130
1131
  			sprintf(buf, "Mesh Network Path Selection Protocol ID: "
  				"0x%02X", cfg[0]);
2a5193119   Johannes Berg   cfg80211/nl80211:...
1132
1133
1134
1135
  			iwe.u.data.length = strlen(buf);
  			current_ev = iwe_stream_add_point(info, current_ev,
  							  end_buf,
  							  &iwe, buf);
76aa5e704   Rui Paulo   mac80211: update ...
1136
1137
  			sprintf(buf, "Path Selection Metric ID: 0x%02X",
  				cfg[1]);
2a5193119   Johannes Berg   cfg80211/nl80211:...
1138
1139
1140
1141
  			iwe.u.data.length = strlen(buf);
  			current_ev = iwe_stream_add_point(info, current_ev,
  							  end_buf,
  							  &iwe, buf);
76aa5e704   Rui Paulo   mac80211: update ...
1142
1143
  			sprintf(buf, "Congestion Control Mode ID: 0x%02X",
  				cfg[2]);
2a5193119   Johannes Berg   cfg80211/nl80211:...
1144
1145
1146
1147
  			iwe.u.data.length = strlen(buf);
  			current_ev = iwe_stream_add_point(info, current_ev,
  							  end_buf,
  							  &iwe, buf);
76aa5e704   Rui Paulo   mac80211: update ...
1148
  			sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
2a5193119   Johannes Berg   cfg80211/nl80211:...
1149
1150
1151
1152
  			iwe.u.data.length = strlen(buf);
  			current_ev = iwe_stream_add_point(info, current_ev,
  							  end_buf,
  							  &iwe, buf);
76aa5e704   Rui Paulo   mac80211: update ...
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
  			sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
  			iwe.u.data.length = strlen(buf);
  			current_ev = iwe_stream_add_point(info, current_ev,
  							  end_buf,
  							  &iwe, buf);
  			sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
  			iwe.u.data.length = strlen(buf);
  			current_ev = iwe_stream_add_point(info, current_ev,
  							  end_buf,
  							  &iwe, buf);
  			sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
2a5193119   Johannes Berg   cfg80211/nl80211:...
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
  			iwe.u.data.length = strlen(buf);
  			current_ev = iwe_stream_add_point(info, current_ev,
  							  end_buf,
  							  &iwe, buf);
  			kfree(buf);
  			break;
  		case WLAN_EID_SUPP_RATES:
  		case WLAN_EID_EXT_SUPP_RATES:
  			/* display all supported rates in readable format */
  			p = current_ev + iwe_stream_lcp_len(info);
  
  			memset(&iwe, 0, sizeof(iwe));
  			iwe.cmd = SIOCGIWRATE;
  			/* Those two flags are ignored... */
  			iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
  
  			for (i = 0; i < ie[1]; i++) {
  				iwe.u.bitrate.value =
  					((ie[i + 2] & 0x7f) * 500000);
  				p = iwe_stream_add_value(info, current_ev, p,
  						end_buf, &iwe, IW_EV_PARAM_LEN);
  			}
  			current_ev = p;
  			break;
  		}
  		rem -= ie[1] + 2;
  		ie += ie[1] + 2;
  	}
f64f9e719   Joe Perches   net: Move && and ...
1192
1193
  	if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) ||
  	    ismesh) {
2a5193119   Johannes Berg   cfg80211/nl80211:...
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
  		memset(&iwe, 0, sizeof(iwe));
  		iwe.cmd = SIOCGIWMODE;
  		if (ismesh)
  			iwe.u.mode = IW_MODE_MESH;
  		else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
  			iwe.u.mode = IW_MODE_MASTER;
  		else
  			iwe.u.mode = IW_MODE_ADHOC;
  		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
  						  &iwe, IW_EV_UINT_LEN);
  	}
  
  	buf = kmalloc(30, GFP_ATOMIC);
  	if (buf) {
  		memset(&iwe, 0, sizeof(iwe));
  		iwe.cmd = IWEVCUSTOM;
  		sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf));
  		iwe.u.data.length = strlen(buf);
  		current_ev = iwe_stream_add_point(info, current_ev, end_buf,
  						  &iwe, buf);
  		memset(&iwe, 0, sizeof(iwe));
  		iwe.cmd = IWEVCUSTOM;
cb3a8eec0   Dan Williams   cfg80211: age sca...
1216
1217
  		sprintf(buf, " Last beacon: %ums ago",
  			elapsed_jiffies_msecs(bss->ts));
2a5193119   Johannes Berg   cfg80211/nl80211:...
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
  		iwe.u.data.length = strlen(buf);
  		current_ev = iwe_stream_add_point(info, current_ev,
  						  end_buf, &iwe, buf);
  		kfree(buf);
  	}
  
  	ieee80211_scan_add_ies(info, &bss->pub, &current_ev, end_buf);
  
  	return current_ev;
  }
  
  
  static int ieee80211_scan_results(struct cfg80211_registered_device *dev,
  				  struct iw_request_info *info,
  				  char *buf, size_t len)
  {
  	char *current_ev = buf;
  	char *end_buf = buf + len;
  	struct cfg80211_internal_bss *bss;
  
  	spin_lock_bh(&dev->bss_lock);
  	cfg80211_bss_expire(dev);
  
  	list_for_each_entry(bss, &dev->bss_list, list) {
  		if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
  			spin_unlock_bh(&dev->bss_lock);
  			return -E2BIG;
  		}
77965c970   Johannes Berg   cfg80211: clean u...
1246
1247
  		current_ev = ieee80211_bss(&dev->wiphy, info, bss,
  					   current_ev, end_buf);
2a5193119   Johannes Berg   cfg80211/nl80211:...
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
  	}
  	spin_unlock_bh(&dev->bss_lock);
  	return current_ev - buf;
  }
  
  
  int cfg80211_wext_giwscan(struct net_device *dev,
  			  struct iw_request_info *info,
  			  struct iw_point *data, char *extra)
  {
  	struct cfg80211_registered_device *rdev;
  	int res;
  
  	if (!netif_running(dev))
  		return -ENETDOWN;
463d01832   Johannes Berg   cfg80211: make aw...
1263
  	rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
2a5193119   Johannes Berg   cfg80211/nl80211:...
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
  
  	if (IS_ERR(rdev))
  		return PTR_ERR(rdev);
  
  	if (rdev->scan_req) {
  		res = -EAGAIN;
  		goto out;
  	}
  
  	res = ieee80211_scan_results(rdev, info, extra, data->length);
  	data->length = 0;
  	if (res >= 0) {
  		data->length = res;
  		res = 0;
  	}
  
   out:
4d0c8aead   Johannes Berg   cfg80211: properl...
1281
  	cfg80211_unlock_rdev(rdev);
2a5193119   Johannes Berg   cfg80211/nl80211:...
1282
1283
  	return res;
  }
ba44cb722   Johannes Berg   cfg80211: mark al...
1284
  EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan);
2a5193119   Johannes Berg   cfg80211/nl80211:...
1285
  #endif