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.c158
1 files changed, 110 insertions, 48 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 0c2cbbebca95..978cac3414b5 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -100,8 +100,10 @@ static void bss_release(struct kref *ref)
100 if (bss->pub.free_priv) 100 if (bss->pub.free_priv)
101 bss->pub.free_priv(&bss->pub); 101 bss->pub.free_priv(&bss->pub);
102 102
103 if (bss->ies_allocated) 103 if (bss->beacon_ies_allocated)
104 kfree(bss->pub.information_elements); 104 kfree(bss->pub.beacon_ies);
105 if (bss->proberesp_ies_allocated)
106 kfree(bss->pub.proberesp_ies);
105 107
106 BUG_ON(atomic_read(&bss->hold)); 108 BUG_ON(atomic_read(&bss->hold));
107 109
@@ -141,9 +143,9 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
141 dev->bss_generation++; 143 dev->bss_generation++;
142} 144}
143 145
144static u8 *find_ie(u8 num, u8 *ies, int len) 146const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
145{ 147{
146 while (len > 2 && ies[0] != num) { 148 while (len > 2 && ies[0] != eid) {
147 len -= ies[1] + 2; 149 len -= ies[1] + 2;
148 ies += ies[1] + 2; 150 ies += ies[1] + 2;
149 } 151 }
@@ -153,11 +155,12 @@ static u8 *find_ie(u8 num, u8 *ies, int len)
153 return NULL; 155 return NULL;
154 return ies; 156 return ies;
155} 157}
158EXPORT_SYMBOL(cfg80211_find_ie);
156 159
157static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) 160static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
158{ 161{
159 const u8 *ie1 = find_ie(num, ies1, len1); 162 const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
160 const u8 *ie2 = find_ie(num, ies2, len2); 163 const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
161 int r; 164 int r;
162 165
163 if (!ie1 && !ie2) 166 if (!ie1 && !ie2)
@@ -183,9 +186,9 @@ static bool is_bss(struct cfg80211_bss *a,
183 if (!ssid) 186 if (!ssid)
184 return true; 187 return true;
185 188
186 ssidie = find_ie(WLAN_EID_SSID, 189 ssidie = cfg80211_find_ie(WLAN_EID_SSID,
187 a->information_elements, 190 a->information_elements,
188 a->len_information_elements); 191 a->len_information_elements);
189 if (!ssidie) 192 if (!ssidie)
190 return false; 193 return false;
191 if (ssidie[1] != ssid_len) 194 if (ssidie[1] != ssid_len)
@@ -202,9 +205,9 @@ static bool is_mesh(struct cfg80211_bss *a,
202 if (!is_zero_ether_addr(a->bssid)) 205 if (!is_zero_ether_addr(a->bssid))
203 return false; 206 return false;
204 207
205 ie = find_ie(WLAN_EID_MESH_ID, 208 ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
206 a->information_elements, 209 a->information_elements,
207 a->len_information_elements); 210 a->len_information_elements);
208 if (!ie) 211 if (!ie)
209 return false; 212 return false;
210 if (ie[1] != meshidlen) 213 if (ie[1] != meshidlen)
@@ -212,9 +215,9 @@ static bool is_mesh(struct cfg80211_bss *a,
212 if (memcmp(ie + 2, meshid, meshidlen)) 215 if (memcmp(ie + 2, meshid, meshidlen))
213 return false; 216 return false;
214 217
215 ie = find_ie(WLAN_EID_MESH_CONFIG, 218 ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
216 a->information_elements, 219 a->information_elements,
217 a->len_information_elements); 220 a->len_information_elements);
218 if (!ie) 221 if (!ie)
219 return false; 222 return false;
220 if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) 223 if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
@@ -375,8 +378,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
375 378
376static struct cfg80211_internal_bss * 379static struct cfg80211_internal_bss *
377cfg80211_bss_update(struct cfg80211_registered_device *dev, 380cfg80211_bss_update(struct cfg80211_registered_device *dev,
378 struct cfg80211_internal_bss *res, 381 struct cfg80211_internal_bss *res)
379 bool overwrite)
380{ 382{
381 struct cfg80211_internal_bss *found = NULL; 383 struct cfg80211_internal_bss *found = NULL;
382 const u8 *meshid, *meshcfg; 384 const u8 *meshid, *meshcfg;
@@ -394,11 +396,12 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
394 396
395 if (is_zero_ether_addr(res->pub.bssid)) { 397 if (is_zero_ether_addr(res->pub.bssid)) {
396 /* must be mesh, verify */ 398 /* must be mesh, verify */
397 meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements, 399 meshid = cfg80211_find_ie(WLAN_EID_MESH_ID,
398 res->pub.len_information_elements); 400 res->pub.information_elements,
399 meshcfg = find_ie(WLAN_EID_MESH_CONFIG, 401 res->pub.len_information_elements);
400 res->pub.information_elements, 402 meshcfg = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
401 res->pub.len_information_elements); 403 res->pub.information_elements,
404 res->pub.len_information_elements);
402 if (!meshid || !meshcfg || 405 if (!meshid || !meshcfg ||
403 meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) { 406 meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) {
404 /* bogus mesh */ 407 /* bogus mesh */
@@ -418,28 +421,64 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
418 found->pub.capability = res->pub.capability; 421 found->pub.capability = res->pub.capability;
419 found->ts = res->ts; 422 found->ts = res->ts;
420 423
421 /* overwrite IEs */ 424 /* Update IEs */
422 if (overwrite) { 425 if (res->pub.proberesp_ies) {
423 size_t used = dev->wiphy.bss_priv_size + sizeof(*res); 426 size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
424 size_t ielen = res->pub.len_information_elements; 427 size_t ielen = res->pub.len_proberesp_ies;
428
429 if (found->pub.proberesp_ies &&
430 !found->proberesp_ies_allocated &&
431 ksize(found) >= used + ielen) {
432 memcpy(found->pub.proberesp_ies,
433 res->pub.proberesp_ies, ielen);
434 found->pub.len_proberesp_ies = ielen;
435 } else {
436 u8 *ies = found->pub.proberesp_ies;
437
438 if (found->proberesp_ies_allocated)
439 ies = krealloc(ies, ielen, GFP_ATOMIC);
440 else
441 ies = kmalloc(ielen, GFP_ATOMIC);
442
443 if (ies) {
444 memcpy(ies, res->pub.proberesp_ies,
445 ielen);
446 found->proberesp_ies_allocated = true;
447 found->pub.proberesp_ies = ies;
448 found->pub.len_proberesp_ies = ielen;
449 }
450 }
425 451
426 if (!found->ies_allocated && ksize(found) >= used + ielen) { 452 /* Override possible earlier Beacon frame IEs */
427 memcpy(found->pub.information_elements, 453 found->pub.information_elements =
428 res->pub.information_elements, ielen); 454 found->pub.proberesp_ies;
429 found->pub.len_information_elements = ielen; 455 found->pub.len_information_elements =
456 found->pub.len_proberesp_ies;
457 }
458 if (res->pub.beacon_ies) {
459 size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
460 size_t ielen = res->pub.len_beacon_ies;
461
462 if (found->pub.beacon_ies &&
463 !found->beacon_ies_allocated &&
464 ksize(found) >= used + ielen) {
465 memcpy(found->pub.beacon_ies,
466 res->pub.beacon_ies, ielen);
467 found->pub.len_beacon_ies = ielen;
430 } else { 468 } else {
431 u8 *ies = found->pub.information_elements; 469 u8 *ies = found->pub.beacon_ies;
432 470
433 if (found->ies_allocated) 471 if (found->beacon_ies_allocated)
434 ies = krealloc(ies, ielen, GFP_ATOMIC); 472 ies = krealloc(ies, ielen, GFP_ATOMIC);
435 else 473 else
436 ies = kmalloc(ielen, GFP_ATOMIC); 474 ies = kmalloc(ielen, GFP_ATOMIC);
437 475
438 if (ies) { 476 if (ies) {
439 memcpy(ies, res->pub.information_elements, ielen); 477 memcpy(ies, res->pub.beacon_ies,
440 found->ies_allocated = true; 478 ielen);
441 found->pub.information_elements = ies; 479 found->beacon_ies_allocated = true;
442 found->pub.len_information_elements = ielen; 480 found->pub.beacon_ies = ies;
481 found->pub.len_beacon_ies = ielen;
443 } 482 }
444 } 483 }
445 } 484 }
@@ -489,14 +528,26 @@ cfg80211_inform_bss(struct wiphy *wiphy,
489 res->pub.tsf = timestamp; 528 res->pub.tsf = timestamp;
490 res->pub.beacon_interval = beacon_interval; 529 res->pub.beacon_interval = beacon_interval;
491 res->pub.capability = capability; 530 res->pub.capability = capability;
492 /* point to after the private area */ 531 /*
493 res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; 532 * Since we do not know here whether the IEs are from a Beacon or Probe
494 memcpy(res->pub.information_elements, ie, ielen); 533 * Response frame, we need to pick one of the options and only use it
495 res->pub.len_information_elements = ielen; 534 * with the driver that does not provide the full Beacon/Probe Response
535 * frame. Use Beacon frame pointer to avoid indicating that this should
536 * override the information_elements pointer should we have received an
537 * earlier indication of Probe Response data.
538 *
539 * The initial buffer for the IEs is allocated with the BSS entry and
540 * is located after the private area.
541 */
542 res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz;
543 memcpy(res->pub.beacon_ies, ie, ielen);
544 res->pub.len_beacon_ies = ielen;
545 res->pub.information_elements = res->pub.beacon_ies;
546 res->pub.len_information_elements = res->pub.len_beacon_ies;
496 547
497 kref_init(&res->ref); 548 kref_init(&res->ref);
498 549
499 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, 0); 550 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
500 if (!res) 551 if (!res)
501 return NULL; 552 return NULL;
502 553
@@ -517,7 +568,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
517 struct cfg80211_internal_bss *res; 568 struct cfg80211_internal_bss *res;
518 size_t ielen = len - offsetof(struct ieee80211_mgmt, 569 size_t ielen = len - offsetof(struct ieee80211_mgmt,
519 u.probe_resp.variable); 570 u.probe_resp.variable);
520 bool overwrite;
521 size_t privsz = wiphy->bss_priv_size; 571 size_t privsz = wiphy->bss_priv_size;
522 572
523 if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC && 573 if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC &&
@@ -538,16 +588,28 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
538 res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); 588 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); 589 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); 590 res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
541 /* point to after the private area */ 591 /*
542 res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; 592 * 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); 593 * is located after the private area.
544 res->pub.len_information_elements = ielen; 594 */
595 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
596 res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz;
597 memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable,
598 ielen);
599 res->pub.len_proberesp_ies = ielen;
600 res->pub.information_elements = res->pub.proberesp_ies;
601 res->pub.len_information_elements = res->pub.len_proberesp_ies;
602 } else {
603 res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz;
604 memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen);
605 res->pub.len_beacon_ies = ielen;
606 res->pub.information_elements = res->pub.beacon_ies;
607 res->pub.len_information_elements = res->pub.len_beacon_ies;
608 }
545 609
546 kref_init(&res->ref); 610 kref_init(&res->ref);
547 611
548 overwrite = ieee80211_is_probe_resp(mgmt->frame_control); 612 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
549
550 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite);
551 if (!res) 613 if (!res)
552 return NULL; 614 return NULL;
553 615