diff options
author | Jouni Malinen <j@w1.fi> | 2010-01-06 09:19:24 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-01-12 13:51:28 -0500 |
commit | 34a6eddbabd704b3c7dae9362234552267573be2 (patch) | |
tree | 661184452aa597e4f1034561b3ec9b267ad2e196 | |
parent | 80a112ffe8dbada25f3780ecc4beebf23451d755 (diff) |
cfg80211: Store IEs from both Beacon and Probe Response frames
Store information elements from Beacon and Probe Response frames in
separate buffers to allow both sets to be made available through
nl80211. This allows user space applications to get access to IEs from
Beacon frames even if we have received Probe Response frames from the
BSS. Previously, the IEs from Probe Response frames would have
overridden the IEs from Beacon frames.
This feature is of somewhat limited use since most protocols include
the same (or extended) information in Probe Response frames. However,
there are couple of exceptions where the IEs from Beacon frames could
be of some use: TIM IE is only included in Beacon frames (and it would
be needed to figure out the DTIM period used in the BSS) and at least
some implementations of Wireless Provisioning Services seem to include
the full IE only in Beacon frames).
The new BSS attribute for scan results is added to allow both the IE
sets to be delivered. This is done in a way that maintains the
previously used behavior for applications that are not aware of the
new NL80211_BSS_BEACON_IES attribute.
Signed-off-by: Jouni Malinen <j@w1.fi>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-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 | ||