aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/scan.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r--net/wireless/scan.c168
1 files changed, 118 insertions, 50 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 0c2cbbebca95..5ca8c7180141 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -4,6 +4,7 @@
4 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> 4 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
5 */ 5 */
6#include <linux/kernel.h> 6#include <linux/kernel.h>
7#include <linux/slab.h>
7#include <linux/module.h> 8#include <linux/module.h>
8#include <linux/netdevice.h> 9#include <linux/netdevice.h>
9#include <linux/wireless.h> 10#include <linux/wireless.h>
@@ -100,8 +101,10 @@ static void bss_release(struct kref *ref)
100 if (bss->pub.free_priv) 101 if (bss->pub.free_priv)
101 bss->pub.free_priv(&bss->pub); 102 bss->pub.free_priv(&bss->pub);
102 103
103 if (bss->ies_allocated) 104 if (bss->beacon_ies_allocated)
104 kfree(bss->pub.information_elements); 105 kfree(bss->pub.beacon_ies);
106 if (bss->proberesp_ies_allocated)
107 kfree(bss->pub.proberesp_ies);
105 108
106 BUG_ON(atomic_read(&bss->hold)); 109 BUG_ON(atomic_read(&bss->hold));
107 110
@@ -141,9 +144,9 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
141 dev->bss_generation++; 144 dev->bss_generation++;
142} 145}
143 146
144static u8 *find_ie(u8 num, u8 *ies, int len) 147const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
145{ 148{
146 while (len > 2 && ies[0] != num) { 149 while (len > 2 && ies[0] != eid) {
147 len -= ies[1] + 2; 150 len -= ies[1] + 2;
148 ies += ies[1] + 2; 151 ies += ies[1] + 2;
149 } 152 }
@@ -153,11 +156,12 @@ static u8 *find_ie(u8 num, u8 *ies, int len)
153 return NULL; 156 return NULL;
154 return ies; 157 return ies;
155} 158}
159EXPORT_SYMBOL(cfg80211_find_ie);
156 160
157static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) 161static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
158{ 162{
159 const u8 *ie1 = find_ie(num, ies1, len1); 163 const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
160 const u8 *ie2 = find_ie(num, ies2, len2); 164 const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
161 int r; 165 int r;
162 166
163 if (!ie1 && !ie2) 167 if (!ie1 && !ie2)
@@ -183,9 +187,9 @@ static bool is_bss(struct cfg80211_bss *a,
183 if (!ssid) 187 if (!ssid)
184 return true; 188 return true;
185 189
186 ssidie = find_ie(WLAN_EID_SSID, 190 ssidie = cfg80211_find_ie(WLAN_EID_SSID,
187 a->information_elements, 191 a->information_elements,
188 a->len_information_elements); 192 a->len_information_elements);
189 if (!ssidie) 193 if (!ssidie)
190 return false; 194 return false;
191 if (ssidie[1] != ssid_len) 195 if (ssidie[1] != ssid_len)
@@ -202,9 +206,9 @@ static bool is_mesh(struct cfg80211_bss *a,
202 if (!is_zero_ether_addr(a->bssid)) 206 if (!is_zero_ether_addr(a->bssid))
203 return false; 207 return false;
204 208
205 ie = find_ie(WLAN_EID_MESH_ID, 209 ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
206 a->information_elements, 210 a->information_elements,
207 a->len_information_elements); 211 a->len_information_elements);
208 if (!ie) 212 if (!ie)
209 return false; 213 return false;
210 if (ie[1] != meshidlen) 214 if (ie[1] != meshidlen)
@@ -212,9 +216,9 @@ static bool is_mesh(struct cfg80211_bss *a,
212 if (memcmp(ie + 2, meshid, meshidlen)) 216 if (memcmp(ie + 2, meshid, meshidlen))
213 return false; 217 return false;
214 218
215 ie = find_ie(WLAN_EID_MESH_CONFIG, 219 ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
216 a->information_elements, 220 a->information_elements,
217 a->len_information_elements); 221 a->len_information_elements);
218 if (!ie) 222 if (!ie)
219 return false; 223 return false;
220 if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) 224 if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
@@ -271,6 +275,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
271{ 275{
272 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); 276 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
273 struct cfg80211_internal_bss *bss, *res = NULL; 277 struct cfg80211_internal_bss *bss, *res = NULL;
278 unsigned long now = jiffies;
274 279
275 spin_lock_bh(&dev->bss_lock); 280 spin_lock_bh(&dev->bss_lock);
276 281
@@ -279,6 +284,10 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
279 continue; 284 continue;
280 if (channel && bss->pub.channel != channel) 285 if (channel && bss->pub.channel != channel)
281 continue; 286 continue;
287 /* Don't get expired BSS structs */
288 if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
289 !atomic_read(&bss->hold))
290 continue;
282 if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { 291 if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
283 res = bss; 292 res = bss;
284 kref_get(&res->ref); 293 kref_get(&res->ref);
@@ -375,8 +384,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
375 384
376static struct cfg80211_internal_bss * 385static struct cfg80211_internal_bss *
377cfg80211_bss_update(struct cfg80211_registered_device *dev, 386cfg80211_bss_update(struct cfg80211_registered_device *dev,
378 struct cfg80211_internal_bss *res, 387 struct cfg80211_internal_bss *res)
379 bool overwrite)
380{ 388{
381 struct cfg80211_internal_bss *found = NULL; 389 struct cfg80211_internal_bss *found = NULL;
382 const u8 *meshid, *meshcfg; 390 const u8 *meshid, *meshcfg;
@@ -394,11 +402,12 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
394 402
395 if (is_zero_ether_addr(res->pub.bssid)) { 403 if (is_zero_ether_addr(res->pub.bssid)) {
396 /* must be mesh, verify */ 404 /* must be mesh, verify */
397 meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements, 405 meshid = cfg80211_find_ie(WLAN_EID_MESH_ID,
398 res->pub.len_information_elements); 406 res->pub.information_elements,
399 meshcfg = find_ie(WLAN_EID_MESH_CONFIG, 407 res->pub.len_information_elements);
400 res->pub.information_elements, 408 meshcfg = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
401 res->pub.len_information_elements); 409 res->pub.information_elements,
410 res->pub.len_information_elements);
402 if (!meshid || !meshcfg || 411 if (!meshid || !meshcfg ||
403 meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) { 412 meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) {
404 /* bogus mesh */ 413 /* bogus mesh */
@@ -418,28 +427,64 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
418 found->pub.capability = res->pub.capability; 427 found->pub.capability = res->pub.capability;
419 found->ts = res->ts; 428 found->ts = res->ts;
420 429
421 /* overwrite IEs */ 430 /* Update IEs */
422 if (overwrite) { 431 if (res->pub.proberesp_ies) {
423 size_t used = dev->wiphy.bss_priv_size + sizeof(*res); 432 size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
424 size_t ielen = res->pub.len_information_elements; 433 size_t ielen = res->pub.len_proberesp_ies;
434
435 if (found->pub.proberesp_ies &&
436 !found->proberesp_ies_allocated &&
437 ksize(found) >= used + ielen) {
438 memcpy(found->pub.proberesp_ies,
439 res->pub.proberesp_ies, ielen);
440 found->pub.len_proberesp_ies = ielen;
441 } else {
442 u8 *ies = found->pub.proberesp_ies;
443
444 if (found->proberesp_ies_allocated)
445 ies = krealloc(ies, ielen, GFP_ATOMIC);
446 else
447 ies = kmalloc(ielen, GFP_ATOMIC);
448
449 if (ies) {
450 memcpy(ies, res->pub.proberesp_ies,
451 ielen);
452 found->proberesp_ies_allocated = true;
453 found->pub.proberesp_ies = ies;
454 found->pub.len_proberesp_ies = ielen;
455 }
456 }
425 457
426 if (!found->ies_allocated && ksize(found) >= used + ielen) { 458 /* Override possible earlier Beacon frame IEs */
427 memcpy(found->pub.information_elements, 459 found->pub.information_elements =
428 res->pub.information_elements, ielen); 460 found->pub.proberesp_ies;
429 found->pub.len_information_elements = ielen; 461 found->pub.len_information_elements =
462 found->pub.len_proberesp_ies;
463 }
464 if (res->pub.beacon_ies) {
465 size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
466 size_t ielen = res->pub.len_beacon_ies;
467
468 if (found->pub.beacon_ies &&
469 !found->beacon_ies_allocated &&
470 ksize(found) >= used + ielen) {
471 memcpy(found->pub.beacon_ies,
472 res->pub.beacon_ies, ielen);
473 found->pub.len_beacon_ies = ielen;
430 } else { 474 } else {
431 u8 *ies = found->pub.information_elements; 475 u8 *ies = found->pub.beacon_ies;
432 476
433 if (found->ies_allocated) 477 if (found->beacon_ies_allocated)
434 ies = krealloc(ies, ielen, GFP_ATOMIC); 478 ies = krealloc(ies, ielen, GFP_ATOMIC);
435 else 479 else
436 ies = kmalloc(ielen, GFP_ATOMIC); 480 ies = kmalloc(ielen, GFP_ATOMIC);
437 481
438 if (ies) { 482 if (ies) {
439 memcpy(ies, res->pub.information_elements, ielen); 483 memcpy(ies, res->pub.beacon_ies,
440 found->ies_allocated = true; 484 ielen);
441 found->pub.information_elements = ies; 485 found->beacon_ies_allocated = true;
442 found->pub.len_information_elements = ielen; 486 found->pub.beacon_ies = ies;
487 found->pub.len_beacon_ies = ielen;
443 } 488 }
444 } 489 }
445 } 490 }
@@ -475,7 +520,7 @@ cfg80211_inform_bss(struct wiphy *wiphy,
475 520
476 privsz = wiphy->bss_priv_size; 521 privsz = wiphy->bss_priv_size;
477 522
478 if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC && 523 if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
479 (signal < 0 || signal > 100))) 524 (signal < 0 || signal > 100)))
480 return NULL; 525 return NULL;
481 526
@@ -489,14 +534,26 @@ cfg80211_inform_bss(struct wiphy *wiphy,
489 res->pub.tsf = timestamp; 534 res->pub.tsf = timestamp;
490 res->pub.beacon_interval = beacon_interval; 535 res->pub.beacon_interval = beacon_interval;
491 res->pub.capability = capability; 536 res->pub.capability = capability;
492 /* point to after the private area */ 537 /*
493 res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; 538 * Since we do not know here whether the IEs are from a Beacon or Probe
494 memcpy(res->pub.information_elements, ie, ielen); 539 * Response frame, we need to pick one of the options and only use it
495 res->pub.len_information_elements = ielen; 540 * with the driver that does not provide the full Beacon/Probe Response
541 * frame. Use Beacon frame pointer to avoid indicating that this should
542 * override the information_elements pointer should we have received an
543 * earlier indication of Probe Response data.
544 *
545 * The initial buffer for the IEs is allocated with the BSS entry and
546 * is located after the private area.
547 */
548 res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz;
549 memcpy(res->pub.beacon_ies, ie, ielen);
550 res->pub.len_beacon_ies = ielen;
551 res->pub.information_elements = res->pub.beacon_ies;
552 res->pub.len_information_elements = res->pub.len_beacon_ies;
496 553
497 kref_init(&res->ref); 554 kref_init(&res->ref);
498 555
499 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, 0); 556 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
500 if (!res) 557 if (!res)
501 return NULL; 558 return NULL;
502 559
@@ -517,10 +574,9 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
517 struct cfg80211_internal_bss *res; 574 struct cfg80211_internal_bss *res;
518 size_t ielen = len - offsetof(struct ieee80211_mgmt, 575 size_t ielen = len - offsetof(struct ieee80211_mgmt,
519 u.probe_resp.variable); 576 u.probe_resp.variable);
520 bool overwrite;
521 size_t privsz = wiphy->bss_priv_size; 577 size_t privsz = wiphy->bss_priv_size;
522 578
523 if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC && 579 if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
524 (signal < 0 || signal > 100))) 580 (signal < 0 || signal > 100)))
525 return NULL; 581 return NULL;
526 582
@@ -538,16 +594,28 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
538 res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); 594 res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
539 res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); 595 res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
540 res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); 596 res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
541 /* point to after the private area */ 597 /*
542 res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; 598 * The initial buffer for the IEs is allocated with the BSS entry and
543 memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen); 599 * is located after the private area.
544 res->pub.len_information_elements = ielen; 600 */
601 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
602 res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz;
603 memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable,
604 ielen);
605 res->pub.len_proberesp_ies = ielen;
606 res->pub.information_elements = res->pub.proberesp_ies;
607 res->pub.len_information_elements = res->pub.len_proberesp_ies;
608 } else {
609 res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz;
610 memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen);
611 res->pub.len_beacon_ies = ielen;
612 res->pub.information_elements = res->pub.beacon_ies;
613 res->pub.len_information_elements = res->pub.len_beacon_ies;
614 }
545 615
546 kref_init(&res->ref); 616 kref_init(&res->ref);
547 617
548 overwrite = ieee80211_is_probe_resp(mgmt->frame_control); 618 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
549
550 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite);
551 if (!res) 619 if (!res)
552 return NULL; 620 return NULL;
553 621