提交 4593c4cb 编写于 作者: J Johannes Berg

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: NJohannes Berg <johannes.berg@intel.com>
上级 5622f5bb
...@@ -424,9 +424,21 @@ static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) ...@@ -424,9 +424,21 @@ static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b)
return memcmp(a->bssid, b->bssid, sizeof(a->bssid)); return memcmp(a->bssid, b->bssid, sizeof(a->bssid));
} }
/**
* enum bss_compare_mode - BSS compare mode
* @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
* @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode
* @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode
*/
enum bss_compare_mode {
BSS_CMP_REGULAR,
BSS_CMP_HIDE_ZLEN,
BSS_CMP_HIDE_NUL,
};
static int cmp_bss(struct cfg80211_bss *a, static int cmp_bss(struct cfg80211_bss *a,
struct cfg80211_bss *b, struct cfg80211_bss *b,
bool hide_ssid) enum bss_compare_mode mode)
{ {
const struct cfg80211_bss_ies *a_ies, *b_ies; const struct cfg80211_bss_ies *a_ies, *b_ies;
const u8 *ie1; const u8 *ie1;
...@@ -462,27 +474,36 @@ static int cmp_bss(struct cfg80211_bss *a, ...@@ -462,27 +474,36 @@ static int cmp_bss(struct cfg80211_bss *a,
if (!ie2) if (!ie2)
return 1; return 1;
/* zero-length SSID is used as an indication of the hidden bss */ switch (mode) {
if (hide_ssid && !ie2[1]) case BSS_CMP_HIDE_ZLEN:
return 0; /*
* In ZLEN mode we assume the BSS entry we're
* looking for has a zero-length SSID. So if
* the one we're looking at right now has that,
* return 0. Otherwise, return the difference
* in length, but since we're looking for the
* 0-length it's really equivalent to returning
* the length of the one we're looking at.
*
* No content comparison is needed as we assume
* the content length is zero.
*/
return ie2[1];
case BSS_CMP_REGULAR:
default:
/* sort by length first, then by contents */ /* sort by length first, then by contents */
if (ie1[1] != ie2[1]) if (ie1[1] != ie2[1])
return ie2[1] - ie1[1]; return ie2[1] - ie1[1];
if (!hide_ssid)
return memcmp(ie1 + 2, ie2 + 2, ie1[1]); return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
case BSS_CMP_HIDE_NUL:
/* if (ie1[1] != ie2[1])
* zeroed SSID ie is another indication of a hidden bss; return ie2[1] - ie1[1];
* if it isn't zeroed just return the regular sort value /* this is equivalent to memcmp(zeroes, ie2 + 2, len) */
* to find the next candidate
*/
for (i = 0; i < ie2[1]; i++) for (i = 0; i < ie2[1]; i++)
if (ie2[i + 2]) if (ie2[i + 2])
return memcmp(ie1 + 2, ie2 + 2, ie1[1]); return -1;
return 0; return 0;
}
} }
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
...@@ -564,7 +585,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev, ...@@ -564,7 +585,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,
parent = *p; parent = *p;
tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
cmp = cmp_bss(&bss->pub, &tbss->pub, false); cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR);
if (WARN_ON(!cmp)) { if (WARN_ON(!cmp)) {
/* will sort of leak this BSS */ /* will sort of leak this BSS */
...@@ -584,7 +605,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev, ...@@ -584,7 +605,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,
static struct cfg80211_internal_bss * static struct cfg80211_internal_bss *
rb_find_bss(struct cfg80211_registered_device *dev, rb_find_bss(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *res, struct cfg80211_internal_bss *res,
bool hidden) enum bss_compare_mode mode)
{ {
struct rb_node *n = dev->bss_tree.rb_node; struct rb_node *n = dev->bss_tree.rb_node;
struct cfg80211_internal_bss *bss; struct cfg80211_internal_bss *bss;
...@@ -592,7 +613,7 @@ rb_find_bss(struct cfg80211_registered_device *dev, ...@@ -592,7 +613,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
while (n) { while (n) {
bss = rb_entry(n, struct cfg80211_internal_bss, rbn); bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
r = cmp_bss(&res->pub, &bss->pub, hidden); r = cmp_bss(&res->pub, &bss->pub, mode);
if (r == 0) if (r == 0)
return bss; return bss;
...@@ -642,7 +663,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, ...@@ -642,7 +663,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
return NULL; return NULL;
} }
found = rb_find_bss(dev, tmp, false); found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR);
if (found) { if (found) {
found->pub.beacon_interval = tmp->pub.beacon_interval; found->pub.beacon_interval = tmp->pub.beacon_interval;
...@@ -697,9 +718,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, ...@@ -697,9 +718,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
/* TODO: The code is not trying to update existing probe /* TODO: The code is not trying to update existing probe
* response bss entries when beacon ies are * response bss entries when beacon ies are
* getting changed. */ * getting changed. */
hidden = rb_find_bss(dev, tmp, true); hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN);
if (hidden) {
copy_hidden_ies(tmp, hidden);
} else {
hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_NUL);
if (hidden) if (hidden)
copy_hidden_ies(tmp, hidden); copy_hidden_ies(tmp, hidden);
}
/* /*
* create a copy -- the "res" variable that is passed in * create a copy -- the "res" variable that is passed in
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册