aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/scan.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-02-01 13:20:03 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-02-04 12:57:41 -0500
commit4593c4cbe1c96b3995727dc42f6aa103f4ff5afc (patch)
treebead4ef70dd54b804d560fa1968f05c89917bb6e /net/wireless/scan.c
parent5622f5bb8dd3cafe0e56014ed03e31957d1b4a03 (diff)
cfg80211: fix BSS list hidden SSID lookup
When trying to find a hidden SSID, the lookup function is done wrong; the code is trying to combine the two lookups into one, and as a consequence doesn't always find the entry at all. To understand this, consider a case where multiple BSS entries with the same channel and BSSID exist but have different SSID length. Then comparing against the probe response SSID length is bound to cause problems since the hidden one might be either zeroed out or zero-length. To fix this we need to do two lookups for the two ways to hide SSIDs. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r--net/wireless/scan.c80
1 files changed, 53 insertions, 27 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index cfc4e1aa8dad..d4fe065eeb11 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -424,9 +424,21 @@ static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b)
424 return memcmp(a->bssid, b->bssid, sizeof(a->bssid)); 424 return memcmp(a->bssid, b->bssid, sizeof(a->bssid));
425} 425}
426 426
427/**
428 * enum bss_compare_mode - BSS compare mode
429 * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
430 * @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode
431 * @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode
432 */
433enum bss_compare_mode {
434 BSS_CMP_REGULAR,
435 BSS_CMP_HIDE_ZLEN,
436 BSS_CMP_HIDE_NUL,
437};
438
427static int cmp_bss(struct cfg80211_bss *a, 439static int cmp_bss(struct cfg80211_bss *a,
428 struct cfg80211_bss *b, 440 struct cfg80211_bss *b,
429 bool hide_ssid) 441 enum bss_compare_mode mode)
430{ 442{
431 const struct cfg80211_bss_ies *a_ies, *b_ies; 443 const struct cfg80211_bss_ies *a_ies, *b_ies;
432 const u8 *ie1; 444 const u8 *ie1;
@@ -462,27 +474,36 @@ static int cmp_bss(struct cfg80211_bss *a,
462 if (!ie2) 474 if (!ie2)
463 return 1; 475 return 1;
464 476
465 /* zero-length SSID is used as an indication of the hidden bss */ 477 switch (mode) {
466 if (hide_ssid && !ie2[1]) 478 case BSS_CMP_HIDE_ZLEN:
467 return 0; 479 /*
468 480 * In ZLEN mode we assume the BSS entry we're
469 /* sort by length first, then by contents */ 481 * looking for has a zero-length SSID. So if
470 if (ie1[1] != ie2[1]) 482 * the one we're looking at right now has that,
471 return ie2[1] - ie1[1]; 483 * return 0. Otherwise, return the difference
472 484 * in length, but since we're looking for the
473 if (!hide_ssid) 485 * 0-length it's really equivalent to returning
486 * the length of the one we're looking at.
487 *
488 * No content comparison is needed as we assume
489 * the content length is zero.
490 */
491 return ie2[1];
492 case BSS_CMP_REGULAR:
493 default:
494 /* sort by length first, then by contents */
495 if (ie1[1] != ie2[1])
496 return ie2[1] - ie1[1];
474 return memcmp(ie1 + 2, ie2 + 2, ie1[1]); 497 return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
475 498 case BSS_CMP_HIDE_NUL:
476 /* 499 if (ie1[1] != ie2[1])
477 * zeroed SSID ie is another indication of a hidden bss; 500 return ie2[1] - ie1[1];
478 * if it isn't zeroed just return the regular sort value 501 /* this is equivalent to memcmp(zeroes, ie2 + 2, len) */
479 * to find the next candidate 502 for (i = 0; i < ie2[1]; i++)
480 */ 503 if (ie2[i + 2])
481 for (i = 0; i < ie2[1]; i++) 504 return -1;
482 if (ie2[i + 2]) 505 return 0;
483 return memcmp(ie1 + 2, ie2 + 2, ie1[1]); 506 }
484
485 return 0;
486} 507}
487 508
488struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, 509struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
@@ -564,7 +585,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,
564 parent = *p; 585 parent = *p;
565 tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); 586 tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
566 587
567 cmp = cmp_bss(&bss->pub, &tbss->pub, false); 588 cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR);
568 589
569 if (WARN_ON(!cmp)) { 590 if (WARN_ON(!cmp)) {
570 /* will sort of leak this BSS */ 591 /* will sort of leak this BSS */
@@ -584,7 +605,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,
584static struct cfg80211_internal_bss * 605static struct cfg80211_internal_bss *
585rb_find_bss(struct cfg80211_registered_device *dev, 606rb_find_bss(struct cfg80211_registered_device *dev,
586 struct cfg80211_internal_bss *res, 607 struct cfg80211_internal_bss *res,
587 bool hidden) 608 enum bss_compare_mode mode)
588{ 609{
589 struct rb_node *n = dev->bss_tree.rb_node; 610 struct rb_node *n = dev->bss_tree.rb_node;
590 struct cfg80211_internal_bss *bss; 611 struct cfg80211_internal_bss *bss;
@@ -592,7 +613,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
592 613
593 while (n) { 614 while (n) {
594 bss = rb_entry(n, struct cfg80211_internal_bss, rbn); 615 bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
595 r = cmp_bss(&res->pub, &bss->pub, hidden); 616 r = cmp_bss(&res->pub, &bss->pub, mode);
596 617
597 if (r == 0) 618 if (r == 0)
598 return bss; 619 return bss;
@@ -642,7 +663,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
642 return NULL; 663 return NULL;
643 } 664 }
644 665
645 found = rb_find_bss(dev, tmp, false); 666 found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR);
646 667
647 if (found) { 668 if (found) {
648 found->pub.beacon_interval = tmp->pub.beacon_interval; 669 found->pub.beacon_interval = tmp->pub.beacon_interval;
@@ -697,9 +718,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
697 /* TODO: The code is not trying to update existing probe 718 /* TODO: The code is not trying to update existing probe
698 * response bss entries when beacon ies are 719 * response bss entries when beacon ies are
699 * getting changed. */ 720 * getting changed. */
700 hidden = rb_find_bss(dev, tmp, true); 721 hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN);
701 if (hidden) 722 if (hidden) {
702 copy_hidden_ies(tmp, hidden); 723 copy_hidden_ies(tmp, hidden);
724 } else {
725 hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_NUL);
726 if (hidden)
727 copy_hidden_ies(tmp, hidden);
728 }
703 729
704 /* 730 /*
705 * create a copy -- the "res" variable that is passed in 731 * create a copy -- the "res" variable that is passed in