diff options
-rw-r--r-- | include/linux/nl80211.h | 10 | ||||
-rw-r--r-- | include/net/cfg80211.h | 12 | ||||
-rw-r--r-- | net/wireless/core.h | 3 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 4 | ||||
-rw-r--r-- | net/wireless/scan.c | 120 |
5 files changed, 116 insertions, 33 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 7a1c8c145b22..127a73015760 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h | |||
@@ -1378,13 +1378,20 @@ enum nl80211_channel_type { | |||
1378 | * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) | 1378 | * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) |
1379 | * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) | 1379 | * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) |
1380 | * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the | 1380 | * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the |
1381 | * raw information elements from the probe response/beacon (bin) | 1381 | * raw information elements from the probe response/beacon (bin); |
1382 | * if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are | ||
1383 | * from a Probe Response frame; otherwise they are from a Beacon frame. | ||
1384 | * However, if the driver does not indicate the source of the IEs, these | ||
1385 | * IEs may be from either frame subtype. | ||
1382 | * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon | 1386 | * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon |
1383 | * in mBm (100 * dBm) (s32) | 1387 | * in mBm (100 * dBm) (s32) |
1384 | * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon | 1388 | * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon |
1385 | * in unspecified units, scaled to 0..100 (u8) | 1389 | * in unspecified units, scaled to 0..100 (u8) |
1386 | * @NL80211_BSS_STATUS: status, if this BSS is "used" | 1390 | * @NL80211_BSS_STATUS: status, if this BSS is "used" |
1387 | * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms | 1391 | * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms |
1392 | * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information | ||
1393 | * elements from a Beacon frame (bin); not present if no Beacon frame has | ||
1394 | * yet been received | ||
1388 | * @__NL80211_BSS_AFTER_LAST: internal | 1395 | * @__NL80211_BSS_AFTER_LAST: internal |
1389 | * @NL80211_BSS_MAX: highest BSS attribute | 1396 | * @NL80211_BSS_MAX: highest BSS attribute |
1390 | */ | 1397 | */ |
@@ -1400,6 +1407,7 @@ enum nl80211_bss { | |||
1400 | NL80211_BSS_SIGNAL_UNSPEC, | 1407 | NL80211_BSS_SIGNAL_UNSPEC, |
1401 | NL80211_BSS_STATUS, | 1408 | NL80211_BSS_STATUS, |
1402 | NL80211_BSS_SEEN_MS_AGO, | 1409 | NL80211_BSS_SEEN_MS_AGO, |
1410 | NL80211_BSS_BEACON_IES, | ||
1403 | 1411 | ||
1404 | /* keep last */ | 1412 | /* keep last */ |
1405 | __NL80211_BSS_AFTER_LAST, | 1413 | __NL80211_BSS_AFTER_LAST, |
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0d734413b5fb..2af52704e670 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -626,8 +626,14 @@ enum cfg80211_signal_type { | |||
626 | * @beacon_interval: the beacon interval as from the frame | 626 | * @beacon_interval: the beacon interval as from the frame |
627 | * @capability: the capability field in host byte order | 627 | * @capability: the capability field in host byte order |
628 | * @information_elements: the information elements (Note that there | 628 | * @information_elements: the information elements (Note that there |
629 | * is no guarantee that these are well-formed!) | 629 | * is no guarantee that these are well-formed!); this is a pointer to |
630 | * either the beacon_ies or proberesp_ies depending on whether Probe | ||
631 | * Response frame has been received | ||
630 | * @len_information_elements: total length of the information elements | 632 | * @len_information_elements: total length of the information elements |
633 | * @beacon_ies: the information elements from the last Beacon frame | ||
634 | * @len_beacon_ies: total length of the beacon_ies | ||
635 | * @proberesp_ies: the information elements from the last Probe Response frame | ||
636 | * @len_proberesp_ies: total length of the proberesp_ies | ||
631 | * @signal: signal strength value (type depends on the wiphy's signal_type) | 637 | * @signal: signal strength value (type depends on the wiphy's signal_type) |
632 | * @free_priv: function pointer to free private data | 638 | * @free_priv: function pointer to free private data |
633 | * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes | 639 | * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes |
@@ -641,6 +647,10 @@ struct cfg80211_bss { | |||
641 | u16 capability; | 647 | u16 capability; |
642 | u8 *information_elements; | 648 | u8 *information_elements; |
643 | size_t len_information_elements; | 649 | size_t len_information_elements; |
650 | u8 *beacon_ies; | ||
651 | size_t len_beacon_ies; | ||
652 | u8 *proberesp_ies; | ||
653 | size_t len_proberesp_ies; | ||
644 | 654 | ||
645 | s32 signal; | 655 | s32 signal; |
646 | 656 | ||
diff --git a/net/wireless/core.h b/net/wireless/core.h index 30ec95f05b52..2d6a6b9c0c43 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -111,7 +111,8 @@ struct cfg80211_internal_bss { | |||
111 | unsigned long ts; | 111 | unsigned long ts; |
112 | struct kref ref; | 112 | struct kref ref; |
113 | atomic_t hold; | 113 | atomic_t hold; |
114 | bool ies_allocated; | 114 | bool beacon_ies_allocated; |
115 | bool proberesp_ies_allocated; | ||
115 | 116 | ||
116 | /* must be last because of priv member */ | 117 | /* must be last because of priv member */ |
117 | struct cfg80211_bss pub; | 118 | struct cfg80211_bss pub; |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b804062e0179..4af7991a9ec8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -3163,6 +3163,10 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
3163 | NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, | 3163 | NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, |
3164 | res->len_information_elements, | 3164 | res->len_information_elements, |
3165 | res->information_elements); | 3165 | res->information_elements); |
3166 | if (res->beacon_ies && res->len_beacon_ies && | ||
3167 | res->beacon_ies != res->information_elements) | ||
3168 | NLA_PUT(msg, NL80211_BSS_BEACON_IES, | ||
3169 | res->len_beacon_ies, res->beacon_ies); | ||
3166 | if (res->tsf) | 3170 | if (res->tsf) |
3167 | NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); | 3171 | NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); |
3168 | if (res->beacon_interval) | 3172 | if (res->beacon_interval) |
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 | ||