scan.c 37.8 KB
Newer Older
1
2
3
4
5
6
/*
 * cfg80211 scan result handling
 *
 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
 */
#include <linux/kernel.h>
7
#include <linux/slab.h>
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>
15
#include <net/cfg80211-wext.h>
16
17
18
#include <net/iw_handler.h>
#include "core.h"
#include "nl80211.h"
19
#include "wext-compat.h"
20
#include "rdev-ops.h"
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
 * DOC: BSS tree/list structure
 *
 * At the top level, the BSS list is kept in both a list in each
 * registered device (@bss_list) as well as an RB-tree for faster
 * lookup. In the RB-tree, entries can be looked up using their
 * channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID
 * for other BSSes.
 *
 * Due to the possibility of hidden SSIDs, there's a second level
 * structure, the "hidden_list" and "hidden_beacon_bss" pointer.
 * The hidden_list connects all BSSes belonging to a single AP
 * that has a hidden SSID, and connects beacon and probe response
 * entries. For a probe response entry for a hidden SSID, the
 * hidden_beacon_bss pointer points to the BSS struct holding the
 * beacon's information.
 *
 * Reference counting is done for all these references except for
 * the hidden_list, so that a beacon BSS struct that is otherwise
 * not referenced has one reference for being on the bss_list and
 * one for each probe response entry that points to it using the
 * hidden_beacon_bss pointer. When a BSS struct that has such a
 * pointer is get/put, the refcount update is also propagated to
 * the referenced struct, this ensure that it cannot get removed
 * while somebody is using the probe response version.
 *
 * Note that the hidden_beacon_bss pointer never changes, due to
 * the reference counting. Therefore, no locking is needed for
 * it.
 *
 * Also note that the hidden_beacon_bss pointer is only relevant
 * if the driver uses something other than the IEs, e.g. private
 * data stored stored in the BSS struct, since the beacon IEs are
 * also linked into the probe response struct.
 */

58
#define IEEE80211_SCAN_RESULT_EXPIRE	(30 * HZ)
59

60
static void bss_free(struct cfg80211_internal_bss *bss)
61
{
62
	struct cfg80211_bss_ies *ies;
63
64
65
66

	if (WARN_ON(atomic_read(&bss->hold)))
		return;

67
	ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
68
	if (ies && !bss->pub.hidden_beacon_bss)
69
70
71
72
		kfree_rcu(ies, rcu_head);
	ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
	if (ies)
		kfree_rcu(ies, rcu_head);
73

74
75
76
77
78
79
80
	/*
	 * This happens when the module is removed, it doesn't
	 * really matter any more save for completeness
	 */
	if (!list_empty(&bss->hidden_list))
		list_del(&bss->hidden_list);

81
82
83
	kfree(bss);
}

84
85
static inline void bss_ref_get(struct cfg80211_registered_device *dev,
			       struct cfg80211_internal_bss *bss)
Johannes Berg's avatar
Johannes Berg committed
86
{
87
88
89
90
91
92
93
94
95
	lockdep_assert_held(&dev->bss_lock);

	bss->refcount++;
	if (bss->pub.hidden_beacon_bss) {
		bss = container_of(bss->pub.hidden_beacon_bss,
				   struct cfg80211_internal_bss,
				   pub);
		bss->refcount++;
	}
Johannes Berg's avatar
Johannes Berg committed
96
97
}

98
99
static inline void bss_ref_put(struct cfg80211_registered_device *dev,
			       struct cfg80211_internal_bss *bss)
Johannes Berg's avatar
Johannes Berg committed
100
{
101
102
103
104
105
106
107
108
109
110
111
112
113
114
	lockdep_assert_held(&dev->bss_lock);

	if (bss->pub.hidden_beacon_bss) {
		struct cfg80211_internal_bss *hbss;
		hbss = container_of(bss->pub.hidden_beacon_bss,
				    struct cfg80211_internal_bss,
				    pub);
		hbss->refcount--;
		if (hbss->refcount == 0)
			bss_free(hbss);
	}
	bss->refcount--;
	if (bss->refcount == 0)
		bss_free(bss);
Johannes Berg's avatar
Johannes Berg committed
115
116
}

117
static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
118
119
				  struct cfg80211_internal_bss *bss)
{
120
121
	lockdep_assert_held(&dev->bss_lock);

122
123
124
125
126
127
128
129
130
131
132
133
134
135
	if (!list_empty(&bss->hidden_list)) {
		/*
		 * don't remove the beacon entry if it has
		 * probe responses associated with it
		 */
		if (!bss->pub.hidden_beacon_bss)
			return false;
		/*
		 * if it's a probe response entry break its
		 * link to the other entries in the group
		 */
		list_del_init(&bss->hidden_list);
	}

136
137
	list_del_init(&bss->list);
	rb_erase(&bss->rbn, &dev->bss_tree);
138
139
	bss_ref_put(dev, bss);
	return true;
140
141
}

142
143
144
145
146
147
static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
				  unsigned long expire_time)
{
	struct cfg80211_internal_bss *bss, *tmp;
	bool expired = false;

148
149
	lockdep_assert_held(&dev->bss_lock);

150
151
152
153
154
155
	list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
		if (atomic_read(&bss->hold))
			continue;
		if (!time_after(expire_time, bss->ts))
			continue;

156
157
		if (__cfg80211_unlink_bss(dev, bss))
			expired = true;
158
159
160
161
162
163
	}

	if (expired)
		dev->bss_generation++;
}

164
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
165
{
Johannes Berg's avatar
Johannes Berg committed
166
	struct cfg80211_scan_request *request;
Johannes Berg's avatar
Johannes Berg committed
167
	struct wireless_dev *wdev;
Johannes Berg's avatar
Johannes Berg committed
168
#ifdef CONFIG_CFG80211_WEXT
169
170
171
	union iwreq_data wrqu;
#endif

172
173
	ASSERT_RDEV_LOCK(rdev);

Johannes Berg's avatar
Johannes Berg committed
174
175
	request = rdev->scan_req;

176
177
178
	if (!request)
		return;

Johannes Berg's avatar
Johannes Berg committed
179
	wdev = request->wdev;
180

181
182
183
184
185
	/*
	 * This must be before sending the other events!
	 * Otherwise, wpa_supplicant gets completely confused with
	 * wext events.
	 */
Johannes Berg's avatar
Johannes Berg committed
186
187
	if (wdev->netdev)
		cfg80211_sme_scan_done(wdev->netdev);
188

189
	if (request->aborted) {
Johannes Berg's avatar
Johannes Berg committed
190
		nl80211_send_scan_aborted(rdev, wdev);
191
192
193
194
195
196
197
	} else {
		if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
			/* flush entries from previous scans */
			spin_lock_bh(&rdev->bss_lock);
			__cfg80211_bss_expire(rdev, request->scan_start);
			spin_unlock_bh(&rdev->bss_lock);
		}
Johannes Berg's avatar
Johannes Berg committed
198
		nl80211_send_scan_done(rdev, wdev);
199
	}
200

Johannes Berg's avatar
Johannes Berg committed
201
#ifdef CONFIG_CFG80211_WEXT
Johannes Berg's avatar
Johannes Berg committed
202
	if (wdev->netdev && !request->aborted) {
203
204
		memset(&wrqu, 0, sizeof(wrqu));

Johannes Berg's avatar
Johannes Berg committed
205
		wireless_send_event(wdev->netdev, SIOCGIWSCAN, &wrqu, NULL);
206
207
208
	}
#endif

Johannes Berg's avatar
Johannes Berg committed
209
210
	if (wdev->netdev)
		dev_put(wdev->netdev);
211

212
	rdev->scan_req = NULL;
213
214
215
216
217
218
219
220
221
222
223

	/*
	 * 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);
224
}
Johannes Berg's avatar
Johannes Berg committed
225

226
227
228
229
230
231
232
233
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);
234
	___cfg80211_scan_done(rdev, false);
235
236
237
	cfg80211_unlock_rdev(rdev);
}

Johannes Berg's avatar
Johannes Berg committed
238
239
void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
{
240
	trace_cfg80211_scan_done(request, aborted);
Johannes Berg's avatar
Johannes Berg committed
241
242
243
	WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);

	request->aborted = aborted;
244
	queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk);
Johannes Berg's avatar
Johannes Berg committed
245
}
246
247
EXPORT_SYMBOL(cfg80211_scan_done);

248
249
250
void __cfg80211_sched_scan_results(struct work_struct *wk)
{
	struct cfg80211_registered_device *rdev;
251
	struct cfg80211_sched_scan_request *request;
252
253
254
255

	rdev = container_of(wk, struct cfg80211_registered_device,
			    sched_scan_results_wk);

256
257
	request = rdev->sched_scan_req;

258
	mutex_lock(&rdev->sched_scan_mtx);
259
260

	/* we don't have sched_scan_req anymore if the scan is stopping */
261
262
263
264
265
266
267
268
269
270
271
	if (request) {
		if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
			/* flush entries from previous scans */
			spin_lock_bh(&rdev->bss_lock);
			__cfg80211_bss_expire(rdev, request->scan_start);
			spin_unlock_bh(&rdev->bss_lock);
			request->scan_start =
				jiffies + msecs_to_jiffies(request->interval);
		}
		nl80211_send_sched_scan_results(rdev, request->dev);
	}
272

273
	mutex_unlock(&rdev->sched_scan_mtx);
274
275
276
277
}

void cfg80211_sched_scan_results(struct wiphy *wiphy)
{
278
	trace_cfg80211_sched_scan_results(wiphy);
279
280
281
282
283
284
285
	/* 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);

286
void cfg80211_sched_scan_stopped(struct wiphy *wiphy)
287
{
288
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
289

290
291
	trace_cfg80211_sched_scan_stopped(wiphy);

292
	mutex_lock(&rdev->sched_scan_mtx);
293
	__cfg80211_stop_sched_scan(rdev, true);
294
	mutex_unlock(&rdev->sched_scan_mtx);
295
296
297
298
299
300
301
302
}
EXPORT_SYMBOL(cfg80211_sched_scan_stopped);

int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
			       bool driver_initiated)
{
	struct net_device *dev;

303
	lockdep_assert_held(&rdev->sched_scan_mtx);
304
305

	if (!rdev->sched_scan_req)
306
		return -ENOENT;
307
308
309

	dev = rdev->sched_scan_req->dev;

310
	if (!driver_initiated) {
311
		int err = rdev_sched_scan_stop(rdev, dev);
312
313
314
		if (err)
			return err;
	}
315
316
317
318
319
320

	nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED);

	kfree(rdev->sched_scan_req);
	rdev->sched_scan_req = NULL;

321
	return 0;
322
323
}

324
325
326
327
328
329
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);

330
	spin_lock_bh(&dev->bss_lock);
331
	list_for_each_entry(bss, &dev->bss_list, list)
332
		bss->ts -= age_jiffies;
333
	spin_unlock_bh(&dev->bss_lock);
334
335
}

336
337
void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
{
338
	__cfg80211_bss_expire(dev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
339
340
}

341
const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
342
{
343
	while (len > 2 && ies[0] != eid) {
344
345
346
347
348
349
350
351
352
		len -= ies[1] + 2;
		ies += ies[1] + 2;
	}
	if (len < 2)
		return NULL;
	if (len < 2 + ies[1])
		return NULL;
	return ies;
}
353
EXPORT_SYMBOL(cfg80211_find_ie);
354

355
356
357
358
359
360
361
362
363
364
365
366
367
368
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;

		ie = (struct ieee80211_vendor_ie *)pos;
369
370
371
372
373
374
375

		/* make sure we can access ie->len */
		BUILD_BUG_ON(offsetof(struct ieee80211_vendor_ie, len) != 1);

		if (ie->len < sizeof(*ie))
			goto cont;

376
377
378
		ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2];
		if (ie_oui == oui && ie->oui_type == oui_type)
			return pos;
379
cont:
380
381
382
383
384
385
		pos += 2 + ie->len;
	}
	return NULL;
}
EXPORT_SYMBOL(cfg80211_find_vendor_ie);

386
static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
387
388
		   const u8 *ssid, size_t ssid_len)
{
389
	const struct cfg80211_bss_ies *ies;
390
391
	const u8 *ssidie;

392
	if (bssid && !ether_addr_equal(a->bssid, bssid))
393
394
		return false;

395
396
397
	if (!ssid)
		return true;

398
399
400
401
	ies = rcu_access_pointer(a->ies);
	if (!ies)
		return false;
	ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
402
403
404
405
406
407
408
	if (!ssidie)
		return false;
	if (ssidie[1] != ssid_len)
		return false;
	return memcmp(ssidie + 2, ssid, ssid_len) == 0;
}

409
410
411
412
413
414
415
416
417
418
419
420
/**
 * enum bss_compare_mode - BSS compare mode
 * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
 * @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode
 * @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode
 */
enum bss_compare_mode {
	BSS_CMP_REGULAR,
	BSS_CMP_HIDE_ZLEN,
	BSS_CMP_HIDE_NUL,
};

421
static int cmp_bss(struct cfg80211_bss *a,
422
		   struct cfg80211_bss *b,
423
		   enum bss_compare_mode mode)
424
{
425
	const struct cfg80211_bss_ies *a_ies, *b_ies;
426
427
	const u8 *ie1 = NULL;
	const u8 *ie2 = NULL;
428
	int i, r;
429

430
431
	if (a->channel != b->channel)
		return b->channel->center_freq - a->channel->center_freq;
432

433
434
435
436
437
438
439
	a_ies = rcu_access_pointer(a->ies);
	if (!a_ies)
		return -1;
	b_ies = rcu_access_pointer(b->ies);
	if (!b_ies)
		return 1;

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
	if (WLAN_CAPABILITY_IS_STA_BSS(a->capability))
		ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID,
				       a_ies->data, a_ies->len);
	if (WLAN_CAPABILITY_IS_STA_BSS(b->capability))
		ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID,
				       b_ies->data, b_ies->len);
	if (ie1 && ie2) {
		int mesh_id_cmp;

		if (ie1[1] == ie2[1])
			mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]);
		else
			mesh_id_cmp = ie2[1] - ie1[1];

		ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
				       a_ies->data, a_ies->len);
		ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
				       b_ies->data, b_ies->len);
		if (ie1 && ie2) {
			if (mesh_id_cmp)
				return mesh_id_cmp;
			if (ie1[1] != ie2[1])
				return ie2[1] - ie1[1];
			return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
		}
	}

	/*
	 * we can't use compare_ether_addr here since we need a < > operator.
	 * The binary return value of compare_ether_addr isn't enough
	 */
	r = memcmp(a->bssid, b->bssid, sizeof(a->bssid));
	if (r)
		return r;

475
476
	ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
	ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
477

478
479
480
	if (!ie1 && !ie2)
		return 0;

Johannes Berg's avatar
Johannes Berg committed
481
	/*
482
483
484
	 * Note that with "hide_ssid", the function returns a match if
	 * the already-present BSS ("b") is a hidden SSID beacon for
	 * the new BSS ("a").
Johannes Berg's avatar
Johannes Berg committed
485
	 */
486
487
488
489
490
491
492

	/* sort missing IE before (left of) present IE */
	if (!ie1)
		return -1;
	if (!ie2)
		return 1;

493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
	switch (mode) {
	case BSS_CMP_HIDE_ZLEN:
		/*
		 * In ZLEN mode we assume the BSS entry we're
		 * looking for has a zero-length SSID. So if
		 * the one we're looking at right now has that,
		 * return 0. Otherwise, return the difference
		 * in length, but since we're looking for the
		 * 0-length it's really equivalent to returning
		 * the length of the one we're looking at.
		 *
		 * No content comparison is needed as we assume
		 * the content length is zero.
		 */
		return ie2[1];
	case BSS_CMP_REGULAR:
	default:
		/* sort by length first, then by contents */
		if (ie1[1] != ie2[1])
			return ie2[1] - ie1[1];
513
		return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
514
515
516
517
518
519
520
521
522
	case BSS_CMP_HIDE_NUL:
		if (ie1[1] != ie2[1])
			return ie2[1] - ie1[1];
		/* this is equivalent to memcmp(zeroes, ie2 + 2, len) */
		for (i = 0; i < ie2[1]; i++)
			if (ie2[i + 2])
				return -1;
		return 0;
	}
523
524
}

525
526
527
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
				      struct ieee80211_channel *channel,
				      const u8 *bssid,
528
529
				      const u8 *ssid, size_t ssid_len,
				      u16 capa_mask, u16 capa_val)
530
531
532
{
	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
	struct cfg80211_internal_bss *bss, *res = NULL;
533
	unsigned long now = jiffies;
534

535
536
537
	trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, capa_mask,
			       capa_val);

538
539
540
	spin_lock_bh(&dev->bss_lock);

	list_for_each_entry(bss, &dev->bss_list, list) {
541
542
		if ((bss->pub.capability & capa_mask) != capa_val)
			continue;
543
544
		if (channel && bss->pub.channel != channel)
			continue;
545
546
547
548
		/* Don't get expired BSS structs */
		if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
		    !atomic_read(&bss->hold))
			continue;
549
550
		if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
			res = bss;
551
			bss_ref_get(dev, res);
552
553
554
555
556
557
558
			break;
		}
	}

	spin_unlock_bh(&dev->bss_lock);
	if (!res)
		return NULL;
559
	trace_cfg80211_return_bss(&res->pub);
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
	return &res->pub;
}
EXPORT_SYMBOL(cfg80211_get_bss);

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);

576
		cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR);
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594

		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,
595
	    struct cfg80211_internal_bss *res,
596
	    enum bss_compare_mode mode)
597
598
599
600
601
602
603
{
	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);
604
		r = cmp_bss(&res->pub, &bss->pub, mode);
605
606
607
608
609
610
611
612
613
614
615
616

		if (r == 0)
			return bss;
		else if (r < 0)
			n = n->rb_left;
		else
			n = n->rb_right;
	}

	return NULL;
}

617
618
static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
				   struct cfg80211_internal_bss *new)
619
{
620
	const struct cfg80211_bss_ies *ies;
621
622
623
624
	struct cfg80211_internal_bss *bss;
	const u8 *ie;
	int i, ssidlen;
	u8 fold = 0;
625

626
	ies = rcu_access_pointer(new->pub.beacon_ies);
627
	if (WARN_ON(!ies))
628
		return false;
629

630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
	ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
	if (!ie) {
		/* nothing to do */
		return true;
	}

	ssidlen = ie[1];
	for (i = 0; i < ssidlen; i++)
		fold |= ie[2 + i];

	if (fold) {
		/* not a hidden SSID */
		return true;
	}

	/* This is the bad part ... */

	list_for_each_entry(bss, &dev->bss_list, list) {
		if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))
			continue;
		if (bss->pub.channel != new->pub.channel)
			continue;
		if (rcu_access_pointer(bss->pub.beacon_ies))
			continue;
		ies = rcu_access_pointer(bss->pub.ies);
		if (!ies)
			continue;
		ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
		if (!ie)
			continue;
		if (ssidlen && ie[1] != ssidlen)
			continue;
		/* that would be odd ... */
		if (bss->pub.beacon_ies)
			continue;
		if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss))
			continue;
		if (WARN_ON_ONCE(!list_empty(&bss->hidden_list)))
			list_del(&bss->hidden_list);
		/* combine them */
		list_add(&bss->hidden_list, &new->hidden_list);
		bss->pub.hidden_beacon_bss = &new->pub;
		new->refcount += bss->refcount;
		rcu_assign_pointer(bss->pub.beacon_ies,
				   new->pub.beacon_ies);
	}

	return true;
678
679
}

680
681
static struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *dev,
682
		    struct cfg80211_internal_bss *tmp)
683
684
685
{
	struct cfg80211_internal_bss *found = NULL;

686
	if (WARN_ON(!tmp->pub.channel))
687
688
		return NULL;

689
	tmp->ts = jiffies;
690
691
692

	spin_lock_bh(&dev->bss_lock);

693
694
695
696
697
	if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
		spin_unlock_bh(&dev->bss_lock);
		return NULL;
	}

698
	found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR);
699

700
	if (found) {
701
702
703
704
		found->pub.beacon_interval = tmp->pub.beacon_interval;
		found->pub.signal = tmp->pub.signal;
		found->pub.capability = tmp->pub.capability;
		found->ts = tmp->ts;
705

706
		/* Update IEs */
707
708
709
710
		if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
			const struct cfg80211_bss_ies *old;

			old = rcu_access_pointer(found->pub.proberesp_ies);
711

712
713
			rcu_assign_pointer(found->pub.proberesp_ies,
					   tmp->pub.proberesp_ies);
714
			/* Override possible earlier Beacon frame IEs */
715
716
717
718
719
720
			rcu_assign_pointer(found->pub.ies,
					   tmp->pub.proberesp_ies);
			if (old)
				kfree_rcu((struct cfg80211_bss_ies *)old,
					  rcu_head);
		} else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
721
			const struct cfg80211_bss_ies *old;
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
			struct cfg80211_internal_bss *bss;

			if (found->pub.hidden_beacon_bss &&
			    !list_empty(&found->hidden_list)) {
				/*
				 * The found BSS struct is one of the probe
				 * response members of a group, but we're
				 * receiving a beacon (beacon_ies in the tmp
				 * bss is used). This can only mean that the
				 * AP changed its beacon from not having an
				 * SSID to showing it, which is confusing so
				 * drop this information.
				 */
				goto drop;
			}
737

738
739
740
741
			old = rcu_access_pointer(found->pub.beacon_ies);

			rcu_assign_pointer(found->pub.beacon_ies,
					   tmp->pub.beacon_ies);
742
743

			/* Override IEs if they were from a beacon before */
744
			if (old == rcu_access_pointer(found->pub.ies))
745
746
				rcu_assign_pointer(found->pub.ies,
						   tmp->pub.beacon_ies);
747

748
749
750
751
752
753
754
755
756
757
758
759
			/* Assign beacon IEs to all sub entries */
			list_for_each_entry(bss, &found->hidden_list,
					    hidden_list) {
				const struct cfg80211_bss_ies *ies;

				ies = rcu_access_pointer(bss->pub.beacon_ies);
				WARN_ON(ies != old);

				rcu_assign_pointer(bss->pub.beacon_ies,
						   tmp->pub.beacon_ies);
			}

760
761
762
763
			if (old)
				kfree_rcu((struct cfg80211_bss_ies *)old,
					  rcu_head);
		}
764
	} else {
765
		struct cfg80211_internal_bss *new;
766
		struct cfg80211_internal_bss *hidden;
767
		struct cfg80211_bss_ies *ies;
768

769
770
771
772
773
774
775
776
777
778
779
780
781
782
		/*
		 * create a copy -- the "res" variable that is passed in
		 * is allocated on the stack since it's not needed in the
		 * more common case of an update
		 */
		new = kzalloc(sizeof(*new) + dev->wiphy.bss_priv_size,
			      GFP_ATOMIC);
		if (!new) {
			ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
			if (ies)
				kfree_rcu(ies, rcu_head);
			ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
			if (ies)
				kfree_rcu(ies, rcu_head);
783
			goto drop;
784
785
		}
		memcpy(new, tmp, sizeof(*new));
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
		new->refcount = 1;
		INIT_LIST_HEAD(&new->hidden_list);

		if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
			hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN);
			if (!hidden)
				hidden = rb_find_bss(dev, tmp,
						     BSS_CMP_HIDE_NUL);
			if (hidden) {
				new->pub.hidden_beacon_bss = &hidden->pub;
				list_add(&new->hidden_list,
					 &hidden->hidden_list);
				hidden->refcount++;
				rcu_assign_pointer(new->pub.beacon_ies,
						   hidden->pub.beacon_ies);
			}
		} else {
			/*
			 * Ok so we found a beacon, and don't have an entry. If
			 * it's a beacon with hidden SSID, we might be in for an
			 * expensive search for any probe responses that should
			 * be grouped with this beacon for updates ...
			 */
			if (!cfg80211_combine_bsses(dev, new)) {
				kfree(new);
				goto drop;
			}
		}

815
816
817
		list_add_tail(&new->list, &dev->bss_list);
		rb_insert_bss(dev, new);
		found = new;
818
819
820
	}

	dev->bss_generation++;
821
	bss_ref_get(dev, found);
822
823
824
	spin_unlock_bh(&dev->bss_lock);

	return found;
825
826
827
 drop:
	spin_unlock_bh(&dev->bss_lock);
	return NULL;
828
829
}

830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
static struct ieee80211_channel *
cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
			 struct ieee80211_channel *channel)
{
	const u8 *tmp;
	u32 freq;
	int channel_number = -1;

	tmp = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ie, ielen);
	if (tmp && tmp[1] == 1) {
		channel_number = tmp[2];
	} else {
		tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen);
		if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) {
			struct ieee80211_ht_operation *htop = (void *)(tmp + 2);

			channel_number = htop->primary_chan;
		}
	}

	if (channel_number < 0)
		return channel;

	freq = ieee80211_channel_to_frequency(channel_number, channel->band);
	channel = ieee80211_get_channel(wiphy, freq);
	if (!channel)
		return NULL;
	if (channel->flags & IEEE80211_CHAN_DISABLED)
		return NULL;
	return channel;
}

862
863
864
struct cfg80211_bss*
cfg80211_inform_bss(struct wiphy *wiphy,
		    struct ieee80211_channel *channel,
865
866
		    const u8 *bssid, u64 tsf, u16 capability,
		    u16 beacon_interval, const u8 *ie, size_t ielen,
867
868
		    s32 signal, gfp_t gfp)
{
869
870
	struct cfg80211_bss_ies *ies;
	struct cfg80211_internal_bss tmp = {}, *res;
871
872
873
874

	if (WARN_ON(!wiphy))
		return NULL;

Sujith's avatar
Sujith committed
875
	if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
876
877
878
			(signal < 0 || signal > 100)))
		return NULL;

879
880
881
882
	channel = cfg80211_get_bss_channel(wiphy, ie, ielen, channel);
	if (!channel)
		return NULL;

883
884
885
886
887
	memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
	tmp.pub.channel = channel;
	tmp.pub.signal = signal;
	tmp.pub.beacon_interval = beacon_interval;
	tmp.pub.capability = capability;
888
889
890
891
892
	/*
	 * 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
893
	 * override the IEs pointer should we have received an earlier
894
	 * indication of Probe Response data.
895
	 */
896
897
898
899
	ies = kmalloc(sizeof(*ies) + ielen, gfp);
	if (!ies)
		return NULL;
	ies->len = ielen;
Johannes Berg's avatar
Johannes Berg committed
900
	ies->tsf = tsf;
901
	memcpy(ies->data, ie, ielen);
902

903
904
	rcu_assign_pointer(tmp.pub.beacon_ies, ies);
	rcu_assign_pointer(tmp.pub.ies, ies);
905

906
	res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
907
908
909
910
911
912
	if (!res)
		return NULL;

	if (res->pub.capability & WLAN_CAPABILITY_ESS)
		regulatory_hint_found_beacon(wiphy, channel, gfp);

913
	trace_cfg80211_return_bss(&res->pub);
914
915
916
917
918
	/* cfg80211_bss_update gives us a referenced result */
	return &res->pub;
}
EXPORT_SYMBOL(cfg80211_inform_bss);

919
920
921
922
struct cfg80211_bss *
cfg80211_inform_bss_frame(struct wiphy *wiphy,
			  struct ieee80211_channel *channel,
			  struct ieee80211_mgmt *mgmt, size_t len,
Johannes Berg's avatar
Johannes Berg committed
923
			  s32 signal, gfp_t gfp)
924
{
925
926
	struct cfg80211_internal_bss tmp = {}, *res;
	struct cfg80211_bss_ies *ies;
927
928
	size_t ielen = len - offsetof(struct ieee80211_mgmt,
				      u.probe_resp.variable);
929

930
931
932
	BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
			offsetof(struct ieee80211_mgmt, u.beacon.variable));

933
934
	trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal);

935
936
937
938
939
	if (WARN_ON(!mgmt))
		return NULL;

	if (WARN_ON(!wiphy))
		return NULL;
940

Sujith's avatar
Sujith committed
941
	if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
Hila Gonen's avatar
Hila Gonen committed
942
		    (signal < 0 || signal > 100)))
943
944
		return NULL;

945
	if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
946
947
		return NULL;

948
949
950
951
952
	channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
					   ielen, channel);
	if (!channel)
		return NULL;

953
954
	ies = kmalloc(sizeof(*ies) + ielen, gfp);
	if (!ies)
955
		return NULL;
956
	ies->len = ielen;
Johannes Berg's avatar
Johannes Berg committed
957
	ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
958
	memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
959

960
961
962
963
964
965
966
967
968
969
970
971
972
	if (ieee80211_is_probe_resp(mgmt->frame_control))
		rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
	else
		rcu_assign_pointer(tmp.pub.beacon_ies, ies);
	rcu_assign_pointer(tmp.pub.ies, ies);
	
	memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
	tmp.pub.channel = channel;
	tmp.pub.signal = signal;
	tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
	tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);

	res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
973
974
975
	if (!res)
		return NULL;

976
977
978
	if (res->pub.capability & WLAN_CAPABILITY_ESS)
		regulatory_hint_found_beacon(wiphy, channel, gfp);

979
	trace_cfg80211_return_bss(&res->pub);
980
981
982
983
984
	/* cfg80211_bss_update gives us a referenced result */
	return &res->pub;
}
EXPORT_SYMBOL(cfg80211_inform_bss_frame);

985
void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
986
{
987
	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
988
989
990
991
992
993
	struct cfg80211_internal_bss *bss;

	if (!pub)
		return;

	bss = container_of(pub, struct cfg80211_internal_bss, pub);
994
995
996
997

	spin_lock_bh(&dev->bss_lock);
	bss_ref_get(dev, bss);
	spin_unlock_bh(&dev->bss_lock);
998
999
1000
}
EXPORT_SYMBOL(cfg80211_ref_bss);

1001
void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
1002
{
1003
	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
1004
1005
1006
1007
1008
1009
	struct cfg80211_internal_bss *bss;

	if (!pub)
		return;

	bss = container_of(pub, struct cfg80211_internal_bss, pub);
1010
1011
1012
1013

	spin_lock_bh(&dev->bss_lock);
	bss_ref_put(dev, bss);
	spin_unlock_bh(&dev->bss_lock);
1014
1015
1016
}
EXPORT_SYMBOL(cfg80211_put_bss);

1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
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);
1028
	if (!list_empty(&bss->list)) {
1029
1030
		if (__cfg80211_unlink_bss(dev, bss))
			dev->bss_generation++;
1031
	}
1032
1033
1034
1035
	spin_unlock_bh(&dev->bss_lock);
}
EXPORT_SYMBOL(cfg80211_unlink_bss);

Johannes Berg's avatar
Johannes Berg committed
1036
#ifdef CONFIG_CFG80211_WEXT
1037
1038
1039
1040
1041
1042
1043
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;
1044
	struct cfg80211_scan_request *creq = NULL;
1045
1046
1047
1048
1049
1050
	int i, err, n_channels = 0;
	enum ieee80211_band band;

	if (!netif_running(dev))
		return -ENETDOWN;

1051
1052
1053
	if (wrqu->data.length == sizeof(struct iw_scan_req))
		wreq = (struct iw_scan_req *)extra;

1054
	rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065

	if (IS_ERR(rdev))
		return PTR_ERR(rdev);

	if (rdev->scan_req) {
		err = -EBUSY;
		goto out;
	}

	wiphy = &rdev->wiphy;

1066
1067
1068
1069
1070
1071
1072
1073
	/* 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;
	}
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083

	creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
		       n_channels * sizeof(void *),
		       GFP_ATOMIC);
	if (!creq) {
		err = -ENOMEM;
		goto out;
	}

	creq->wiphy = wiphy;
Johannes Berg's avatar
Johannes Berg committed
1084
	creq->wdev = dev->ieee80211_ptr;
1085
1086
	/* SSIDs come after channels */
	creq->ssids = (void *)&creq->channels[n_channels];
1087
1088
	creq->n_channels = n_channels;
	creq->n_ssids = 1;
1089
	creq->scan_start = jiffies;
1090

1091
	/* translate "Scan on frequencies" request */
1092
1093
1094
	i = 0;
	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
		int j;
1095

1096
1097
		if (!wiphy->bands[band])
			continue;
1098

1099
		for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
1100
1101
1102
1103
			/* ignore disabled channels */
			if (wiphy->bands[band]->channels[j].flags &
						IEEE80211_CHAN_DISABLED)
				continue;
1104
1105
1106
1107
1108
1109
1110
1111
1112

			/* 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++) {
1113
					int wext_freq = cfg80211_wext_freq(wiphy, &wreq->channel_list[k]);
1114
1115
1116
1117
1118
1119
1120
					if (wext_freq == wiphy_freq)
						goto wext_freq_found;
				}
				goto wext_freq_not_found;
			}

		wext_freq_found:
1121
1122
			creq->channels[i] = &wiphy->bands[band]->channels[j];
			i++;
1123
		wext_freq_not_found: ;
1124
1125
		}
	}
1126
1127
1128
1129
1130
	/* No channels found? */
	if (!i) {
		err = -EINVAL;
		goto out;
	}
1131

1132
1133
	/* Set real number of channels specified in creq->channels[] */
	creq->n_channels = i;
1134

1135
1136
	/* translate "Scan for SSID" request */
	if (wreq) {
1137
		if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
1138
1139
1140
1141
			if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
				err = -EINVAL;
				goto out;
			}
1142
1143
1144
1145
1146
1147
1148
			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;
	}

1149
	for (i = 0; i < IEEE80211_NUM_BANDS; i++)
1150
1151
		if (wiphy->bands[i])
			creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
1152

1153
	rdev->scan_req = creq;
1154
	err = rdev_scan(rdev, creq);
1155
1156
	if (err) {
		rdev->scan_req = NULL;
1157
		/* creq will be freed below */
1158
	} else {
Johannes Berg's avatar
Johannes Berg committed
1159
		nl80211_send_scan_start(rdev, dev->ieee80211_ptr);
1160
1161
		/* creq now owned by driver */
		creq = NULL;
1162
1163
		dev_hold(dev);
	}
1164
 out:
1165
	kfree(creq);
1166
	cfg80211_unlock_rdev(rdev);
1167
1168
	return err;
}
1169
EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
1170
1171

static void ieee80211_scan_add_ies(struct iw_request_info *info,
1172
				   const struct cfg80211_bss_ies *ies,
1173
1174
				   char **current_ev, char *end_buf)
{
1175
	const u8 *pos, *end, *next;
1176
1177
	struct iw_event iwe;

1178
	if (!ies)
1179
1180
1181
1182
1183
1184
		return;

	/*
	 * If needed, fragment the IEs buffer (at IE boundaries) into short
	 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
	 */
1185
1186
	pos = ies->data;
	end = pos + ies->len;
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196

	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,
1197
1198
						   end_buf, &iwe,
						   (void *)pos);
1199
1200
1201
1202
1203
1204
1205
1206
1207

		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,
1208
1209
						   end_buf, &iwe,
						   (void *)pos);
1210
1211
1212
1213
	}
}

static char *
Johannes Berg's avatar
Johannes Berg committed
1214
1215
1216
ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
	      struct cfg80211_internal_bss *bss, char *current_ev,
	      char *end_buf)
1217
{
1218
	const struct cfg80211_bss_ies *ies;
1219
	struct iw_event iwe;
1220
	const u8 *ie;
1221
	u8 *buf, *cfg, *p;
1222
	int rem, i, sig;
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
	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