scan.c revision 09f97e0fc4ae81f151bd76b97e28d2af429c1427
1/*
2 * cfg80211 scan result handling
3 *
4 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
5 */
6#include <linux/kernel.h>
7#include <linux/module.h>
8#include <linux/netdevice.h>
9#include <linux/wireless.h>
10#include <linux/nl80211.h>
11#include <linux/etherdevice.h>
12#include <net/arp.h>
13#include <net/cfg80211.h>
14#include <net/iw_handler.h>
15#include "core.h"
16#include "nl80211.h"
17
18#define IEEE80211_SCAN_RESULT_EXPIRE	(15 * HZ)
19
20void __cfg80211_scan_done(struct work_struct *wk)
21{
22	struct cfg80211_registered_device *rdev;
23	struct cfg80211_scan_request *request;
24	struct net_device *dev;
25#ifdef CONFIG_WIRELESS_EXT
26	union iwreq_data wrqu;
27#endif
28
29	rdev = container_of(wk, struct cfg80211_registered_device,
30			    scan_done_wk);
31
32	mutex_lock(&rdev->mtx);
33	request = rdev->scan_req;
34
35	dev = request->dev;
36
37	/*
38	 * This must be before sending the other events!
39	 * Otherwise, wpa_supplicant gets completely confused with
40	 * wext events.
41	 */
42	cfg80211_sme_scan_done(dev);
43
44	if (request->aborted)
45		nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev);
46	else
47		nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev);
48
49#ifdef CONFIG_WIRELESS_EXT
50	if (!request->aborted) {
51		memset(&wrqu, 0, sizeof(wrqu));
52
53		wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
54	}
55#endif
56
57	dev_put(dev);
58
59	cfg80211_unlock_rdev(rdev);
60	wiphy_to_dev(request->wiphy)->scan_req = NULL;
61	kfree(request);
62}
63
64void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
65{
66	WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
67
68	request->aborted = aborted;
69	schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk);
70}
71EXPORT_SYMBOL(cfg80211_scan_done);
72
73static void bss_release(struct kref *ref)
74{
75	struct cfg80211_internal_bss *bss;
76
77	bss = container_of(ref, struct cfg80211_internal_bss, ref);
78	if (bss->pub.free_priv)
79		bss->pub.free_priv(&bss->pub);
80
81	if (bss->ies_allocated)
82		kfree(bss->pub.information_elements);
83
84	BUG_ON(atomic_read(&bss->hold));
85
86	kfree(bss);
87}
88
89/* must hold dev->bss_lock! */
90void cfg80211_bss_age(struct cfg80211_registered_device *dev,
91                      unsigned long age_secs)
92{
93	struct cfg80211_internal_bss *bss;
94	unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
95
96	list_for_each_entry(bss, &dev->bss_list, list) {
97		bss->ts -= age_jiffies;
98	}
99}
100
101/* must hold dev->bss_lock! */
102void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
103{
104	struct cfg80211_internal_bss *bss, *tmp;
105	bool expired = false;
106
107	list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
108		if (atomic_read(&bss->hold))
109			continue;
110		if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
111			continue;
112		list_del(&bss->list);
113		rb_erase(&bss->rbn, &dev->bss_tree);
114		kref_put(&bss->ref, bss_release);
115		expired = true;
116	}
117
118	if (expired)
119		dev->bss_generation++;
120}
121
122static u8 *find_ie(u8 num, u8 *ies, size_t len)
123{
124	while (len > 2 && ies[0] != num) {
125		len -= ies[1] + 2;
126		ies += ies[1] + 2;
127	}
128	if (len < 2)
129		return NULL;
130	if (len < 2 + ies[1])
131		return NULL;
132	return ies;
133}
134
135static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
136{
137	const u8 *ie1 = find_ie(num, ies1, len1);
138	const u8 *ie2 = find_ie(num, ies2, len2);
139	int r;
140
141	if (!ie1 && !ie2)
142		return 0;
143	if (!ie1)
144		return -1;
145
146	r = memcmp(ie1 + 2, ie2 + 2, min(ie1[1], ie2[1]));
147	if (r == 0 && ie1[1] != ie2[1])
148		return ie2[1] - ie1[1];
149	return r;
150}
151
152static bool is_bss(struct cfg80211_bss *a,
153		   const u8 *bssid,
154		   const u8 *ssid, size_t ssid_len)
155{
156	const u8 *ssidie;
157
158	if (bssid && compare_ether_addr(a->bssid, bssid))
159		return false;
160
161	if (!ssid)
162		return true;
163
164	ssidie = find_ie(WLAN_EID_SSID,
165			 a->information_elements,
166			 a->len_information_elements);
167	if (!ssidie)
168		return false;
169	if (ssidie[1] != ssid_len)
170		return false;
171	return memcmp(ssidie + 2, ssid, ssid_len) == 0;
172}
173
174static bool is_mesh(struct cfg80211_bss *a,
175		    const u8 *meshid, size_t meshidlen,
176		    const u8 *meshcfg)
177{
178	const u8 *ie;
179
180	if (!is_zero_ether_addr(a->bssid))
181		return false;
182
183	ie = find_ie(WLAN_EID_MESH_ID,
184		     a->information_elements,
185		     a->len_information_elements);
186	if (!ie)
187		return false;
188	if (ie[1] != meshidlen)
189		return false;
190	if (memcmp(ie + 2, meshid, meshidlen))
191		return false;
192
193	ie = find_ie(WLAN_EID_MESH_CONFIG,
194		     a->information_elements,
195		     a->len_information_elements);
196	if (ie[1] != IEEE80211_MESH_CONFIG_LEN)
197		return false;
198
199	/*
200	 * Ignore mesh capability (last two bytes of the IE) when
201	 * comparing since that may differ between stations taking
202	 * part in the same mesh.
203	 */
204	return memcmp(ie + 2, meshcfg, IEEE80211_MESH_CONFIG_LEN - 2) == 0;
205}
206
207static int cmp_bss(struct cfg80211_bss *a,
208		   struct cfg80211_bss *b)
209{
210	int r;
211
212	if (a->channel != b->channel)
213		return b->channel->center_freq - a->channel->center_freq;
214
215	r = memcmp(a->bssid, b->bssid, ETH_ALEN);
216	if (r)
217		return r;
218
219	if (is_zero_ether_addr(a->bssid)) {
220		r = cmp_ies(WLAN_EID_MESH_ID,
221			    a->information_elements,
222			    a->len_information_elements,
223			    b->information_elements,
224			    b->len_information_elements);
225		if (r)
226			return r;
227		return cmp_ies(WLAN_EID_MESH_CONFIG,
228			       a->information_elements,
229			       a->len_information_elements,
230			       b->information_elements,
231			       b->len_information_elements);
232	}
233
234	return cmp_ies(WLAN_EID_SSID,
235		       a->information_elements,
236		       a->len_information_elements,
237		       b->information_elements,
238		       b->len_information_elements);
239}
240
241struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
242				      struct ieee80211_channel *channel,
243				      const u8 *bssid,
244				      const u8 *ssid, size_t ssid_len,
245				      u16 capa_mask, u16 capa_val)
246{
247	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
248	struct cfg80211_internal_bss *bss, *res = NULL;
249
250	spin_lock_bh(&dev->bss_lock);
251
252	list_for_each_entry(bss, &dev->bss_list, list) {
253		if ((bss->pub.capability & capa_mask) != capa_val)
254			continue;
255		if (channel && bss->pub.channel != channel)
256			continue;
257		if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
258			res = bss;
259			kref_get(&res->ref);
260			break;
261		}
262	}
263
264	spin_unlock_bh(&dev->bss_lock);
265	if (!res)
266		return NULL;
267	return &res->pub;
268}
269EXPORT_SYMBOL(cfg80211_get_bss);
270
271struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy,
272				       struct ieee80211_channel *channel,
273				       const u8 *meshid, size_t meshidlen,
274				       const u8 *meshcfg)
275{
276	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
277	struct cfg80211_internal_bss *bss, *res = NULL;
278
279	spin_lock_bh(&dev->bss_lock);
280
281	list_for_each_entry(bss, &dev->bss_list, list) {
282		if (channel && bss->pub.channel != channel)
283			continue;
284		if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) {
285			res = bss;
286			kref_get(&res->ref);
287			break;
288		}
289	}
290
291	spin_unlock_bh(&dev->bss_lock);
292	if (!res)
293		return NULL;
294	return &res->pub;
295}
296EXPORT_SYMBOL(cfg80211_get_mesh);
297
298
299static void rb_insert_bss(struct cfg80211_registered_device *dev,
300			  struct cfg80211_internal_bss *bss)
301{
302	struct rb_node **p = &dev->bss_tree.rb_node;
303	struct rb_node *parent = NULL;
304	struct cfg80211_internal_bss *tbss;
305	int cmp;
306
307	while (*p) {
308		parent = *p;
309		tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
310
311		cmp = cmp_bss(&bss->pub, &tbss->pub);
312
313		if (WARN_ON(!cmp)) {
314			/* will sort of leak this BSS */
315			return;
316		}
317
318		if (cmp < 0)
319			p = &(*p)->rb_left;
320		else
321			p = &(*p)->rb_right;
322	}
323
324	rb_link_node(&bss->rbn, parent, p);
325	rb_insert_color(&bss->rbn, &dev->bss_tree);
326}
327
328static struct cfg80211_internal_bss *
329rb_find_bss(struct cfg80211_registered_device *dev,
330	    struct cfg80211_internal_bss *res)
331{
332	struct rb_node *n = dev->bss_tree.rb_node;
333	struct cfg80211_internal_bss *bss;
334	int r;
335
336	while (n) {
337		bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
338		r = cmp_bss(&res->pub, &bss->pub);
339
340		if (r == 0)
341			return bss;
342		else if (r < 0)
343			n = n->rb_left;
344		else
345			n = n->rb_right;
346	}
347
348	return NULL;
349}
350
351static struct cfg80211_internal_bss *
352cfg80211_bss_update(struct cfg80211_registered_device *dev,
353		    struct cfg80211_internal_bss *res,
354		    bool overwrite)
355{
356	struct cfg80211_internal_bss *found = NULL;
357	const u8 *meshid, *meshcfg;
358
359	/*
360	 * The reference to "res" is donated to this function.
361	 */
362
363	if (WARN_ON(!res->pub.channel)) {
364		kref_put(&res->ref, bss_release);
365		return NULL;
366	}
367
368	res->ts = jiffies;
369
370	if (is_zero_ether_addr(res->pub.bssid)) {
371		/* must be mesh, verify */
372		meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements,
373				 res->pub.len_information_elements);
374		meshcfg = find_ie(WLAN_EID_MESH_CONFIG,
375				  res->pub.information_elements,
376				  res->pub.len_information_elements);
377		if (!meshid || !meshcfg ||
378		    meshcfg[1] != IEEE80211_MESH_CONFIG_LEN) {
379			/* bogus mesh */
380			kref_put(&res->ref, bss_release);
381			return NULL;
382		}
383	}
384
385	spin_lock_bh(&dev->bss_lock);
386
387	found = rb_find_bss(dev, res);
388
389	if (found) {
390		found->pub.beacon_interval = res->pub.beacon_interval;
391		found->pub.tsf = res->pub.tsf;
392		found->pub.signal = res->pub.signal;
393		found->pub.capability = res->pub.capability;
394		found->ts = res->ts;
395
396		/* overwrite IEs */
397		if (overwrite) {
398			size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
399			size_t ielen = res->pub.len_information_elements;
400
401			if (!found->ies_allocated && ksize(found) >= used + ielen) {
402				memcpy(found->pub.information_elements,
403				       res->pub.information_elements, ielen);
404				found->pub.len_information_elements = ielen;
405			} else {
406				u8 *ies = found->pub.information_elements;
407
408				if (found->ies_allocated)
409					ies = krealloc(ies, ielen, GFP_ATOMIC);
410				else
411					ies = kmalloc(ielen, GFP_ATOMIC);
412
413				if (ies) {
414					memcpy(ies, res->pub.information_elements, ielen);
415					found->ies_allocated = true;
416					found->pub.information_elements = ies;
417					found->pub.len_information_elements = ielen;
418				}
419			}
420		}
421
422		kref_put(&res->ref, bss_release);
423	} else {
424		/* this "consumes" the reference */
425		list_add_tail(&res->list, &dev->bss_list);
426		rb_insert_bss(dev, res);
427		found = res;
428	}
429
430	dev->bss_generation++;
431	spin_unlock_bh(&dev->bss_lock);
432
433	kref_get(&found->ref);
434	return found;
435}
436
437struct cfg80211_bss*
438cfg80211_inform_bss(struct wiphy *wiphy,
439		    struct ieee80211_channel *channel,
440		    const u8 *bssid,
441		    u64 timestamp, u16 capability, u16 beacon_interval,
442		    const u8 *ie, size_t ielen,
443		    s32 signal, gfp_t gfp)
444{
445	struct cfg80211_internal_bss *res;
446	size_t privsz;
447
448	if (WARN_ON(!wiphy))
449		return NULL;
450
451	privsz = wiphy->bss_priv_size;
452
453	if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC &&
454			(signal < 0 || signal > 100)))
455		return NULL;
456
457	res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
458	if (!res)
459		return NULL;
460
461	memcpy(res->pub.bssid, bssid, ETH_ALEN);
462	res->pub.channel = channel;
463	res->pub.signal = signal;
464	res->pub.tsf = timestamp;
465	res->pub.beacon_interval = beacon_interval;
466	res->pub.capability = capability;
467	/* point to after the private area */
468	res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
469	memcpy(res->pub.information_elements, ie, ielen);
470	res->pub.len_information_elements = ielen;
471
472	kref_init(&res->ref);
473
474	res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, 0);
475	if (!res)
476		return NULL;
477
478	if (res->pub.capability & WLAN_CAPABILITY_ESS)
479		regulatory_hint_found_beacon(wiphy, channel, gfp);
480
481	/* cfg80211_bss_update gives us a referenced result */
482	return &res->pub;
483}
484EXPORT_SYMBOL(cfg80211_inform_bss);
485
486struct cfg80211_bss *
487cfg80211_inform_bss_frame(struct wiphy *wiphy,
488			  struct ieee80211_channel *channel,
489			  struct ieee80211_mgmt *mgmt, size_t len,
490			  s32 signal, gfp_t gfp)
491{
492	struct cfg80211_internal_bss *res;
493	size_t ielen = len - offsetof(struct ieee80211_mgmt,
494				      u.probe_resp.variable);
495	bool overwrite;
496	size_t privsz = wiphy->bss_priv_size;
497
498	if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC &&
499	            (signal < 0 || signal > 100)))
500		return NULL;
501
502	if (WARN_ON(!mgmt || !wiphy ||
503		    len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
504		return NULL;
505
506	res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
507	if (!res)
508		return NULL;
509
510	memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN);
511	res->pub.channel = channel;
512	res->pub.signal = signal;
513	res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
514	res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
515	res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
516	/* point to after the private area */
517	res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
518	memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen);
519	res->pub.len_information_elements = ielen;
520
521	kref_init(&res->ref);
522
523	overwrite = ieee80211_is_probe_resp(mgmt->frame_control);
524
525	res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite);
526	if (!res)
527		return NULL;
528
529	if (res->pub.capability & WLAN_CAPABILITY_ESS)
530		regulatory_hint_found_beacon(wiphy, channel, gfp);
531
532	/* cfg80211_bss_update gives us a referenced result */
533	return &res->pub;
534}
535EXPORT_SYMBOL(cfg80211_inform_bss_frame);
536
537void cfg80211_put_bss(struct cfg80211_bss *pub)
538{
539	struct cfg80211_internal_bss *bss;
540
541	if (!pub)
542		return;
543
544	bss = container_of(pub, struct cfg80211_internal_bss, pub);
545	kref_put(&bss->ref, bss_release);
546}
547EXPORT_SYMBOL(cfg80211_put_bss);
548
549void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
550{
551	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
552	struct cfg80211_internal_bss *bss;
553
554	if (WARN_ON(!pub))
555		return;
556
557	bss = container_of(pub, struct cfg80211_internal_bss, pub);
558
559	spin_lock_bh(&dev->bss_lock);
560
561	list_del(&bss->list);
562	rb_erase(&bss->rbn, &dev->bss_tree);
563
564	spin_unlock_bh(&dev->bss_lock);
565
566	kref_put(&bss->ref, bss_release);
567}
568EXPORT_SYMBOL(cfg80211_unlink_bss);
569
570#ifdef CONFIG_WIRELESS_EXT
571int cfg80211_wext_siwscan(struct net_device *dev,
572			  struct iw_request_info *info,
573			  union iwreq_data *wrqu, char *extra)
574{
575	struct cfg80211_registered_device *rdev;
576	struct wiphy *wiphy;
577	struct iw_scan_req *wreq = NULL;
578	struct cfg80211_scan_request *creq;
579	int i, err, n_channels = 0;
580	enum ieee80211_band band;
581
582	if (!netif_running(dev))
583		return -ENETDOWN;
584
585	rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
586
587	if (IS_ERR(rdev))
588		return PTR_ERR(rdev);
589
590	if (rdev->scan_req) {
591		err = -EBUSY;
592		goto out;
593	}
594
595	wiphy = &rdev->wiphy;
596
597	for (band = 0; band < IEEE80211_NUM_BANDS; band++)
598		if (wiphy->bands[band])
599			n_channels += wiphy->bands[band]->n_channels;
600
601	creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
602		       n_channels * sizeof(void *),
603		       GFP_ATOMIC);
604	if (!creq) {
605		err = -ENOMEM;
606		goto out;
607	}
608
609	creq->wiphy = wiphy;
610	creq->dev = dev;
611	creq->ssids = (void *)(creq + 1);
612	creq->channels = (void *)(creq->ssids + 1);
613	creq->n_channels = n_channels;
614	creq->n_ssids = 1;
615
616	/* all channels */
617	i = 0;
618	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
619		int j;
620		if (!wiphy->bands[band])
621			continue;
622		for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
623			creq->channels[i] = &wiphy->bands[band]->channels[j];
624			i++;
625		}
626	}
627
628	/* translate scan request */
629	if (wrqu->data.length == sizeof(struct iw_scan_req)) {
630		wreq = (struct iw_scan_req *)extra;
631
632		if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
633			if (wreq->essid_len > IEEE80211_MAX_SSID_LEN)
634				return -EINVAL;
635			memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
636			creq->ssids[0].ssid_len = wreq->essid_len;
637		}
638		if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
639			creq->n_ssids = 0;
640	}
641
642	rdev->scan_req = creq;
643	err = rdev->ops->scan(wiphy, dev, creq);
644	if (err) {
645		rdev->scan_req = NULL;
646		kfree(creq);
647	} else {
648		nl80211_send_scan_start(rdev, dev);
649		dev_hold(dev);
650	}
651 out:
652	cfg80211_unlock_rdev(rdev);
653	return err;
654}
655EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
656
657static void ieee80211_scan_add_ies(struct iw_request_info *info,
658				   struct cfg80211_bss *bss,
659				   char **current_ev, char *end_buf)
660{
661	u8 *pos, *end, *next;
662	struct iw_event iwe;
663
664	if (!bss->information_elements ||
665	    !bss->len_information_elements)
666		return;
667
668	/*
669	 * If needed, fragment the IEs buffer (at IE boundaries) into short
670	 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
671	 */
672	pos = bss->information_elements;
673	end = pos + bss->len_information_elements;
674
675	while (end - pos > IW_GENERIC_IE_MAX) {
676		next = pos + 2 + pos[1];
677		while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
678			next = next + 2 + next[1];
679
680		memset(&iwe, 0, sizeof(iwe));
681		iwe.cmd = IWEVGENIE;
682		iwe.u.data.length = next - pos;
683		*current_ev = iwe_stream_add_point(info, *current_ev,
684						   end_buf, &iwe, pos);
685
686		pos = next;
687	}
688
689	if (end > pos) {
690		memset(&iwe, 0, sizeof(iwe));
691		iwe.cmd = IWEVGENIE;
692		iwe.u.data.length = end - pos;
693		*current_ev = iwe_stream_add_point(info, *current_ev,
694						   end_buf, &iwe, pos);
695	}
696}
697
698static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
699{
700	unsigned long end = jiffies;
701
702	if (end >= start)
703		return jiffies_to_msecs(end - start);
704
705	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
706}
707
708static char *
709ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
710	      struct cfg80211_internal_bss *bss, char *current_ev,
711	      char *end_buf)
712{
713	struct iw_event iwe;
714	u8 *buf, *cfg, *p;
715	u8 *ie = bss->pub.information_elements;
716	int rem = bss->pub.len_information_elements, i, sig;
717	bool ismesh = false;
718
719	memset(&iwe, 0, sizeof(iwe));
720	iwe.cmd = SIOCGIWAP;
721	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
722	memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
723	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
724					  IW_EV_ADDR_LEN);
725
726	memset(&iwe, 0, sizeof(iwe));
727	iwe.cmd = SIOCGIWFREQ;
728	iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
729	iwe.u.freq.e = 0;
730	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
731					  IW_EV_FREQ_LEN);
732
733	memset(&iwe, 0, sizeof(iwe));
734	iwe.cmd = SIOCGIWFREQ;
735	iwe.u.freq.m = bss->pub.channel->center_freq;
736	iwe.u.freq.e = 6;
737	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
738					  IW_EV_FREQ_LEN);
739
740	if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
741		memset(&iwe, 0, sizeof(iwe));
742		iwe.cmd = IWEVQUAL;
743		iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
744				     IW_QUAL_NOISE_INVALID |
745				     IW_QUAL_QUAL_UPDATED;
746		switch (wiphy->signal_type) {
747		case CFG80211_SIGNAL_TYPE_MBM:
748			sig = bss->pub.signal / 100;
749			iwe.u.qual.level = sig;
750			iwe.u.qual.updated |= IW_QUAL_DBM;
751			if (sig < -110)		/* rather bad */
752				sig = -110;
753			else if (sig > -40)	/* perfect */
754				sig = -40;
755			/* will give a range of 0 .. 70 */
756			iwe.u.qual.qual = sig + 110;
757			break;
758		case CFG80211_SIGNAL_TYPE_UNSPEC:
759			iwe.u.qual.level = bss->pub.signal;
760			/* will give range 0 .. 100 */
761			iwe.u.qual.qual = bss->pub.signal;
762			break;
763		default:
764			/* not reached */
765			break;
766		}
767		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
768						  &iwe, IW_EV_QUAL_LEN);
769	}
770
771	memset(&iwe, 0, sizeof(iwe));
772	iwe.cmd = SIOCGIWENCODE;
773	if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
774		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
775	else
776		iwe.u.data.flags = IW_ENCODE_DISABLED;
777	iwe.u.data.length = 0;
778	current_ev = iwe_stream_add_point(info, current_ev, end_buf,
779					  &iwe, "");
780
781	while (rem >= 2) {
782		/* invalid data */
783		if (ie[1] > rem - 2)
784			break;
785
786		switch (ie[0]) {
787		case WLAN_EID_SSID:
788			memset(&iwe, 0, sizeof(iwe));
789			iwe.cmd = SIOCGIWESSID;
790			iwe.u.data.length = ie[1];
791			iwe.u.data.flags = 1;
792			current_ev = iwe_stream_add_point(info, current_ev, end_buf,
793							  &iwe, ie + 2);
794			break;
795		case WLAN_EID_MESH_ID:
796			memset(&iwe, 0, sizeof(iwe));
797			iwe.cmd = SIOCGIWESSID;
798			iwe.u.data.length = ie[1];
799			iwe.u.data.flags = 1;
800			current_ev = iwe_stream_add_point(info, current_ev, end_buf,
801							  &iwe, ie + 2);
802			break;
803		case WLAN_EID_MESH_CONFIG:
804			ismesh = true;
805			if (ie[1] != IEEE80211_MESH_CONFIG_LEN)
806				break;
807			buf = kmalloc(50, GFP_ATOMIC);
808			if (!buf)
809				break;
810			cfg = ie + 2;
811			memset(&iwe, 0, sizeof(iwe));
812			iwe.cmd = IWEVCUSTOM;
813			sprintf(buf, "Mesh network (version %d)", cfg[0]);
814			iwe.u.data.length = strlen(buf);
815			current_ev = iwe_stream_add_point(info, current_ev,
816							  end_buf,
817							  &iwe, buf);
818			sprintf(buf, "Path Selection Protocol ID: "
819				"0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
820							cfg[4]);
821			iwe.u.data.length = strlen(buf);
822			current_ev = iwe_stream_add_point(info, current_ev,
823							  end_buf,
824							  &iwe, buf);
825			sprintf(buf, "Path Selection Metric ID: "
826				"0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
827							cfg[8]);
828			iwe.u.data.length = strlen(buf);
829			current_ev = iwe_stream_add_point(info, current_ev,
830							  end_buf,
831							  &iwe, buf);
832			sprintf(buf, "Congestion Control Mode ID: "
833				"0x%02X%02X%02X%02X", cfg[9], cfg[10],
834							cfg[11], cfg[12]);
835			iwe.u.data.length = strlen(buf);
836			current_ev = iwe_stream_add_point(info, current_ev,
837							  end_buf,
838							  &iwe, buf);
839			sprintf(buf, "Channel Precedence: "
840				"0x%02X%02X%02X%02X", cfg[13], cfg[14],
841							cfg[15], cfg[16]);
842			iwe.u.data.length = strlen(buf);
843			current_ev = iwe_stream_add_point(info, current_ev,
844							  end_buf,
845							  &iwe, buf);
846			kfree(buf);
847			break;
848		case WLAN_EID_SUPP_RATES:
849		case WLAN_EID_EXT_SUPP_RATES:
850			/* display all supported rates in readable format */
851			p = current_ev + iwe_stream_lcp_len(info);
852
853			memset(&iwe, 0, sizeof(iwe));
854			iwe.cmd = SIOCGIWRATE;
855			/* Those two flags are ignored... */
856			iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
857
858			for (i = 0; i < ie[1]; i++) {
859				iwe.u.bitrate.value =
860					((ie[i + 2] & 0x7f) * 500000);
861				p = iwe_stream_add_value(info, current_ev, p,
862						end_buf, &iwe, IW_EV_PARAM_LEN);
863			}
864			current_ev = p;
865			break;
866		}
867		rem -= ie[1] + 2;
868		ie += ie[1] + 2;
869	}
870
871	if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
872	    || ismesh) {
873		memset(&iwe, 0, sizeof(iwe));
874		iwe.cmd = SIOCGIWMODE;
875		if (ismesh)
876			iwe.u.mode = IW_MODE_MESH;
877		else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
878			iwe.u.mode = IW_MODE_MASTER;
879		else
880			iwe.u.mode = IW_MODE_ADHOC;
881		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
882						  &iwe, IW_EV_UINT_LEN);
883	}
884
885	buf = kmalloc(30, GFP_ATOMIC);
886	if (buf) {
887		memset(&iwe, 0, sizeof(iwe));
888		iwe.cmd = IWEVCUSTOM;
889		sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf));
890		iwe.u.data.length = strlen(buf);
891		current_ev = iwe_stream_add_point(info, current_ev, end_buf,
892						  &iwe, buf);
893		memset(&iwe, 0, sizeof(iwe));
894		iwe.cmd = IWEVCUSTOM;
895		sprintf(buf, " Last beacon: %ums ago",
896			elapsed_jiffies_msecs(bss->ts));
897		iwe.u.data.length = strlen(buf);
898		current_ev = iwe_stream_add_point(info, current_ev,
899						  end_buf, &iwe, buf);
900		kfree(buf);
901	}
902
903	ieee80211_scan_add_ies(info, &bss->pub, &current_ev, end_buf);
904
905	return current_ev;
906}
907
908
909static int ieee80211_scan_results(struct cfg80211_registered_device *dev,
910				  struct iw_request_info *info,
911				  char *buf, size_t len)
912{
913	char *current_ev = buf;
914	char *end_buf = buf + len;
915	struct cfg80211_internal_bss *bss;
916
917	spin_lock_bh(&dev->bss_lock);
918	cfg80211_bss_expire(dev);
919
920	list_for_each_entry(bss, &dev->bss_list, list) {
921		if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
922			spin_unlock_bh(&dev->bss_lock);
923			return -E2BIG;
924		}
925		current_ev = ieee80211_bss(&dev->wiphy, info, bss,
926					   current_ev, end_buf);
927	}
928	spin_unlock_bh(&dev->bss_lock);
929	return current_ev - buf;
930}
931
932
933int cfg80211_wext_giwscan(struct net_device *dev,
934			  struct iw_request_info *info,
935			  struct iw_point *data, char *extra)
936{
937	struct cfg80211_registered_device *rdev;
938	int res;
939
940	if (!netif_running(dev))
941		return -ENETDOWN;
942
943	rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
944
945	if (IS_ERR(rdev))
946		return PTR_ERR(rdev);
947
948	if (rdev->scan_req) {
949		res = -EAGAIN;
950		goto out;
951	}
952
953	res = ieee80211_scan_results(rdev, info, extra, data->length);
954	data->length = 0;
955	if (res >= 0) {
956		data->length = res;
957		res = 0;
958	}
959
960 out:
961	cfg80211_unlock_rdev(rdev);
962	return res;
963}
964EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan);
965#endif
966