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