Commit dd9dfb9f95e2141db672eb12a1d71892e9e481fb
Committed by
John W. Linville
1 parent
1eb54c8a0f
Exists in
master
and in
6 other branches
cfg80211: merge in beacon ies of hidden bss.
The problem with PSM when a hidden SSID was used was originally reported by Juuso Oikarinen. - When generally scanning, the AP is getting a bss entry with a zero SSID. - When associating, a probe-req is sent to the AP with the SSID, and as a result a probe-response is received with the hidden SSID in place. As a consequence, a second bss entry is created for the AP, now with the real SSID. - After association, mac80211 executes ieee80211_recalc_ps(), but does not switch to powersave because the beacon-ies are missing. As result, the STA does not ever enter PSM. The patch merges in beacon ies of hidden bss from beacon to the probe response, creating a consistent set of ies in place. Patch is depended on "cfg80211: fix cmp_ies" made by Johannes. Signed-off-by: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Showing 1 changed file with 114 additions and 3 deletions Side-by-side Diff
net/wireless/scan.c
... | ... | @@ -355,8 +355,8 @@ |
355 | 355 | sizeof(struct ieee80211_meshconf_ie) - 2) == 0; |
356 | 356 | } |
357 | 357 | |
358 | -static int cmp_bss(struct cfg80211_bss *a, | |
359 | - struct cfg80211_bss *b) | |
358 | +static int cmp_bss_core(struct cfg80211_bss *a, | |
359 | + struct cfg80211_bss *b) | |
360 | 360 | { |
361 | 361 | int r; |
362 | 362 | |
... | ... | @@ -378,7 +378,15 @@ |
378 | 378 | b->len_information_elements); |
379 | 379 | } |
380 | 380 | |
381 | - r = memcmp(a->bssid, b->bssid, ETH_ALEN); | |
381 | + return memcmp(a->bssid, b->bssid, ETH_ALEN); | |
382 | +} | |
383 | + | |
384 | +static int cmp_bss(struct cfg80211_bss *a, | |
385 | + struct cfg80211_bss *b) | |
386 | +{ | |
387 | + int r; | |
388 | + | |
389 | + r = cmp_bss_core(a, b); | |
382 | 390 | if (r) |
383 | 391 | return r; |
384 | 392 | |
... | ... | @@ -389,6 +397,52 @@ |
389 | 397 | b->len_information_elements); |
390 | 398 | } |
391 | 399 | |
400 | +static int cmp_hidden_bss(struct cfg80211_bss *a, | |
401 | + struct cfg80211_bss *b) | |
402 | +{ | |
403 | + const u8 *ie1; | |
404 | + const u8 *ie2; | |
405 | + int i; | |
406 | + int r; | |
407 | + | |
408 | + r = cmp_bss_core(a, b); | |
409 | + if (r) | |
410 | + return r; | |
411 | + | |
412 | + ie1 = cfg80211_find_ie(WLAN_EID_SSID, | |
413 | + a->information_elements, | |
414 | + a->len_information_elements); | |
415 | + ie2 = cfg80211_find_ie(WLAN_EID_SSID, | |
416 | + b->information_elements, | |
417 | + b->len_information_elements); | |
418 | + | |
419 | + /* Key comparator must use same algorithm in any rb-tree | |
420 | + * search function (order is important), otherwise ordering | |
421 | + * of items in the tree is broken and search gives incorrect | |
422 | + * results. This code uses same order as cmp_ies() does. */ | |
423 | + | |
424 | + /* sort missing IE before (left of) present IE */ | |
425 | + if (!ie1) | |
426 | + return -1; | |
427 | + if (!ie2) | |
428 | + return 1; | |
429 | + | |
430 | + /* zero-size SSID is used as an indication of the hidden bss */ | |
431 | + if (!ie2[1]) | |
432 | + return 0; | |
433 | + | |
434 | + /* sort by length first, then by contents */ | |
435 | + if (ie1[1] != ie2[1]) | |
436 | + return ie2[1] - ie1[1]; | |
437 | + | |
438 | + /* zeroed SSID ie is another indication of a hidden bss */ | |
439 | + for (i = 0; i < ie2[1]; i++) | |
440 | + if (ie2[i + 2]) | |
441 | + return -1; | |
442 | + | |
443 | + return 0; | |
444 | +} | |
445 | + | |
392 | 446 | struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, |
393 | 447 | struct ieee80211_channel *channel, |
394 | 448 | const u8 *bssid, |
... | ... | @@ -505,6 +559,48 @@ |
505 | 559 | } |
506 | 560 | |
507 | 561 | static struct cfg80211_internal_bss * |
562 | +rb_find_hidden_bss(struct cfg80211_registered_device *dev, | |
563 | + struct cfg80211_internal_bss *res) | |
564 | +{ | |
565 | + struct rb_node *n = dev->bss_tree.rb_node; | |
566 | + struct cfg80211_internal_bss *bss; | |
567 | + int r; | |
568 | + | |
569 | + while (n) { | |
570 | + bss = rb_entry(n, struct cfg80211_internal_bss, rbn); | |
571 | + r = cmp_hidden_bss(&res->pub, &bss->pub); | |
572 | + | |
573 | + if (r == 0) | |
574 | + return bss; | |
575 | + else if (r < 0) | |
576 | + n = n->rb_left; | |
577 | + else | |
578 | + n = n->rb_right; | |
579 | + } | |
580 | + | |
581 | + return NULL; | |
582 | +} | |
583 | + | |
584 | +static void | |
585 | +copy_hidden_ies(struct cfg80211_internal_bss *res, | |
586 | + struct cfg80211_internal_bss *hidden) | |
587 | +{ | |
588 | + if (unlikely(res->pub.beacon_ies)) | |
589 | + return; | |
590 | + if (WARN_ON(!hidden->pub.beacon_ies)) | |
591 | + return; | |
592 | + | |
593 | + res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC); | |
594 | + if (unlikely(!res->pub.beacon_ies)) | |
595 | + return; | |
596 | + | |
597 | + res->beacon_ies_allocated = true; | |
598 | + res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; | |
599 | + memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, | |
600 | + res->pub.len_beacon_ies); | |
601 | +} | |
602 | + | |
603 | +static struct cfg80211_internal_bss * | |
508 | 604 | cfg80211_bss_update(struct cfg80211_registered_device *dev, |
509 | 605 | struct cfg80211_internal_bss *res) |
510 | 606 | { |
... | ... | @@ -607,6 +703,21 @@ |
607 | 703 | |
608 | 704 | kref_put(&res->ref, bss_release); |
609 | 705 | } else { |
706 | + struct cfg80211_internal_bss *hidden; | |
707 | + | |
708 | + /* First check if the beacon is a probe response from | |
709 | + * a hidden bss. If so, copy beacon ies (with nullified | |
710 | + * ssid) into the probe response bss entry (with real ssid). | |
711 | + * It is required basically for PSM implementation | |
712 | + * (probe responses do not contain tim ie) */ | |
713 | + | |
714 | + /* TODO: The code is not trying to update existing probe | |
715 | + * response bss entries when beacon ies are | |
716 | + * getting changed. */ | |
717 | + hidden = rb_find_hidden_bss(dev, res); | |
718 | + if (hidden) | |
719 | + copy_hidden_ies(res, hidden); | |
720 | + | |
610 | 721 | /* this "consumes" the reference */ |
611 | 722 | list_add_tail(&res->list, &dev->bss_list); |
612 | 723 | rb_insert_bss(dev, res); |