diff options
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r-- | net/wireless/scan.c | 120 |
1 files changed, 90 insertions, 30 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 0c2cbbebca95..06b0231ee5e3 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 | ||
@@ -375,8 +377,7 @@ rb_find_bss(struct cfg80211_registered_device *dev, | |||
375 | 377 | ||
376 | static struct cfg80211_internal_bss * | 378 | static struct cfg80211_internal_bss * |
377 | cfg80211_bss_update(struct cfg80211_registered_device *dev, | 379 | cfg80211_bss_update(struct cfg80211_registered_device *dev, |
378 | struct cfg80211_internal_bss *res, | 380 | struct cfg80211_internal_bss *res) |
379 | bool overwrite) | ||
380 | { | 381 | { |
381 | struct cfg80211_internal_bss *found = NULL; | 382 | struct cfg80211_internal_bss *found = NULL; |
382 | const u8 *meshid, *meshcfg; | 383 | const u8 *meshid, *meshcfg; |
@@ -418,28 +419,64 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
418 | found->pub.capability = res->pub.capability; | 419 | found->pub.capability = res->pub.capability; |
419 | found->ts = res->ts; | 420 | found->ts = res->ts; |
420 | 421 | ||
421 | /* overwrite IEs */ | 422 | /* Update IEs */ |
422 | if (overwrite) { | 423 | if (res->pub.proberesp_ies) { |
423 | size_t used = dev->wiphy.bss_priv_size + sizeof(*res); | 424 | size_t used = dev->wiphy.bss_priv_size + sizeof(*res); |
424 | size_t ielen = res->pub.len_information_elements; | 425 | size_t ielen = res->pub.len_proberesp_ies; |
426 | |||
427 | if (found->pub.proberesp_ies && | ||
428 | !found->proberesp_ies_allocated && | ||
429 | ksize(found) >= used + ielen) { | ||
430 | memcpy(found->pub.proberesp_ies, | ||
431 | res->pub.proberesp_ies, ielen); | ||
432 | found->pub.len_proberesp_ies = ielen; | ||
433 | } else { | ||
434 | u8 *ies = found->pub.proberesp_ies; | ||
435 | |||
436 | if (found->proberesp_ies_allocated) | ||
437 | ies = krealloc(ies, ielen, GFP_ATOMIC); | ||
438 | else | ||
439 | ies = kmalloc(ielen, GFP_ATOMIC); | ||
440 | |||
441 | if (ies) { | ||
442 | memcpy(ies, res->pub.proberesp_ies, | ||
443 | ielen); | ||
444 | found->proberesp_ies_allocated = true; | ||
445 | found->pub.proberesp_ies = ies; | ||
446 | found->pub.len_proberesp_ies = ielen; | ||
447 | } | ||
448 | } | ||
425 | 449 | ||
426 | if (!found->ies_allocated && ksize(found) >= used + ielen) { | 450 | /* Override possible earlier Beacon frame IEs */ |
427 | memcpy(found->pub.information_elements, | 451 | found->pub.information_elements = |
428 | res->pub.information_elements, ielen); | 452 | found->pub.proberesp_ies; |
429 | found->pub.len_information_elements = ielen; | 453 | found->pub.len_information_elements = |
454 | found->pub.len_proberesp_ies; | ||
455 | } | ||
456 | if (res->pub.beacon_ies) { | ||
457 | size_t used = dev->wiphy.bss_priv_size + sizeof(*res); | ||
458 | size_t ielen = res->pub.len_beacon_ies; | ||
459 | |||
460 | if (found->pub.beacon_ies && | ||
461 | !found->beacon_ies_allocated && | ||
462 | ksize(found) >= used + ielen) { | ||
463 | memcpy(found->pub.beacon_ies, | ||
464 | res->pub.beacon_ies, ielen); | ||
465 | found->pub.len_beacon_ies = ielen; | ||
430 | } else { | 466 | } else { |
431 | u8 *ies = found->pub.information_elements; | 467 | u8 *ies = found->pub.beacon_ies; |
432 | 468 | ||
433 | if (found->ies_allocated) | 469 | if (found->beacon_ies_allocated) |
434 | ies = krealloc(ies, ielen, GFP_ATOMIC); | 470 | ies = krealloc(ies, ielen, GFP_ATOMIC); |
435 | else | 471 | else |
436 | ies = kmalloc(ielen, GFP_ATOMIC); | 472 | ies = kmalloc(ielen, GFP_ATOMIC); |
437 | 473 | ||
438 | if (ies) { | 474 | if (ies) { |
439 | memcpy(ies, res->pub.information_elements, ielen); | 475 | memcpy(ies, res->pub.beacon_ies, |
440 | found->ies_allocated = true; | 476 | ielen); |
441 | found->pub.information_elements = ies; | 477 | found->beacon_ies_allocated = true; |
442 | found->pub.len_information_elements = ielen; | 478 | found->pub.beacon_ies = ies; |
479 | found->pub.len_beacon_ies = ielen; | ||
443 | } | 480 | } |
444 | } | 481 | } |
445 | } | 482 | } |
@@ -489,14 +526,26 @@ cfg80211_inform_bss(struct wiphy *wiphy, | |||
489 | res->pub.tsf = timestamp; | 526 | res->pub.tsf = timestamp; |
490 | res->pub.beacon_interval = beacon_interval; | 527 | res->pub.beacon_interval = beacon_interval; |
491 | res->pub.capability = capability; | 528 | res->pub.capability = capability; |
492 | /* point to after the private area */ | 529 | /* |
493 | res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; | 530 | * Since we do not know here whether the IEs are from a Beacon or Probe |
494 | memcpy(res->pub.information_elements, ie, ielen); | 531 | * Response frame, we need to pick one of the options and only use it |
495 | res->pub.len_information_elements = ielen; | 532 | * with the driver that does not provide the full Beacon/Probe Response |
533 | * frame. Use Beacon frame pointer to avoid indicating that this should | ||
534 | * override the information_elements pointer should we have received an | ||
535 | * earlier indication of Probe Response data. | ||
536 | * | ||
537 | * The initial buffer for the IEs is allocated with the BSS entry and | ||
538 | * is located after the private area. | ||
539 | */ | ||
540 | res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; | ||
541 | memcpy(res->pub.beacon_ies, ie, ielen); | ||
542 | res->pub.len_beacon_ies = ielen; | ||
543 | res->pub.information_elements = res->pub.beacon_ies; | ||
544 | res->pub.len_information_elements = res->pub.len_beacon_ies; | ||
496 | 545 | ||
497 | kref_init(&res->ref); | 546 | kref_init(&res->ref); |
498 | 547 | ||
499 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, 0); | 548 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); |
500 | if (!res) | 549 | if (!res) |
501 | return NULL; | 550 | return NULL; |
502 | 551 | ||
@@ -517,7 +566,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
517 | struct cfg80211_internal_bss *res; | 566 | struct cfg80211_internal_bss *res; |
518 | size_t ielen = len - offsetof(struct ieee80211_mgmt, | 567 | size_t ielen = len - offsetof(struct ieee80211_mgmt, |
519 | u.probe_resp.variable); | 568 | u.probe_resp.variable); |
520 | bool overwrite; | ||
521 | size_t privsz = wiphy->bss_priv_size; | 569 | size_t privsz = wiphy->bss_priv_size; |
522 | 570 | ||
523 | if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC && | 571 | if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC && |
@@ -538,16 +586,28 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
538 | res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); | 586 | 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); | 587 | 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); | 588 | res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); |
541 | /* point to after the private area */ | 589 | /* |
542 | res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; | 590 | * 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); | 591 | * is located after the private area. |
544 | res->pub.len_information_elements = ielen; | 592 | */ |
593 | if (ieee80211_is_probe_resp(mgmt->frame_control)) { | ||
594 | res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; | ||
595 | memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, | ||
596 | ielen); | ||
597 | res->pub.len_proberesp_ies = ielen; | ||
598 | res->pub.information_elements = res->pub.proberesp_ies; | ||
599 | res->pub.len_information_elements = res->pub.len_proberesp_ies; | ||
600 | } else { | ||
601 | res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz; | ||
602 | memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen); | ||
603 | res->pub.len_beacon_ies = ielen; | ||
604 | res->pub.information_elements = res->pub.beacon_ies; | ||
605 | res->pub.len_information_elements = res->pub.len_beacon_ies; | ||
606 | } | ||
545 | 607 | ||
546 | kref_init(&res->ref); | 608 | kref_init(&res->ref); |
547 | 609 | ||
548 | overwrite = ieee80211_is_probe_resp(mgmt->frame_control); | 610 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); |
549 | |||
550 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite); | ||
551 | if (!res) | 611 | if (!res) |
552 | return NULL; | 612 | return NULL; |
553 | 613 | ||