Commit dd9dfb9f95e2141db672eb12a1d71892e9e481fb

Authored by Dmitry Tarnyagin
Committed by John W. Linville
1 parent 1eb54c8a0f

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

... ... @@ -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);