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>
This commit is contained in:
Dmitry Tarnyagin 2011-11-04 17:12:07 +01:00 committed by John W. Linville
parent 1eb54c8a0f
commit dd9dfb9f95
1 changed files with 114 additions and 3 deletions

View File

@ -355,8 +355,8 @@ static bool is_mesh(struct cfg80211_bss *a,
sizeof(struct ieee80211_meshconf_ie) - 2) == 0; sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
} }
static int cmp_bss(struct cfg80211_bss *a, static int cmp_bss_core(struct cfg80211_bss *a,
struct cfg80211_bss *b) struct cfg80211_bss *b)
{ {
int r; int r;
@ -378,7 +378,15 @@ static int cmp_bss(struct cfg80211_bss *a,
b->len_information_elements); b->len_information_elements);
} }
r = memcmp(a->bssid, b->bssid, ETH_ALEN); return memcmp(a->bssid, b->bssid, ETH_ALEN);
}
static int cmp_bss(struct cfg80211_bss *a,
struct cfg80211_bss *b)
{
int r;
r = cmp_bss_core(a, b);
if (r) if (r)
return r; return r;
@ -389,6 +397,52 @@ static int cmp_bss(struct cfg80211_bss *a,
b->len_information_elements); b->len_information_elements);
} }
static int cmp_hidden_bss(struct cfg80211_bss *a,
struct cfg80211_bss *b)
{
const u8 *ie1;
const u8 *ie2;
int i;
int r;
r = cmp_bss_core(a, b);
if (r)
return r;
ie1 = cfg80211_find_ie(WLAN_EID_SSID,
a->information_elements,
a->len_information_elements);
ie2 = cfg80211_find_ie(WLAN_EID_SSID,
b->information_elements,
b->len_information_elements);
/* Key comparator must use same algorithm in any rb-tree
* search function (order is important), otherwise ordering
* of items in the tree is broken and search gives incorrect
* results. This code uses same order as cmp_ies() does. */
/* sort missing IE before (left of) present IE */
if (!ie1)
return -1;
if (!ie2)
return 1;
/* zero-size SSID is used as an indication of the hidden bss */
if (!ie2[1])
return 0;
/* sort by length first, then by contents */
if (ie1[1] != ie2[1])
return ie2[1] - ie1[1];
/* zeroed SSID ie is another indication of a hidden bss */
for (i = 0; i < ie2[1]; i++)
if (ie2[i + 2])
return -1;
return 0;
}
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
const u8 *bssid, const u8 *bssid,
@ -504,6 +558,48 @@ rb_find_bss(struct cfg80211_registered_device *dev,
return NULL; return NULL;
} }
static struct cfg80211_internal_bss *
rb_find_hidden_bss(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *res)
{
struct rb_node *n = dev->bss_tree.rb_node;
struct cfg80211_internal_bss *bss;
int r;
while (n) {
bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
r = cmp_hidden_bss(&res->pub, &bss->pub);
if (r == 0)
return bss;
else if (r < 0)
n = n->rb_left;
else
n = n->rb_right;
}
return NULL;
}
static void
copy_hidden_ies(struct cfg80211_internal_bss *res,
struct cfg80211_internal_bss *hidden)
{
if (unlikely(res->pub.beacon_ies))
return;
if (WARN_ON(!hidden->pub.beacon_ies))
return;
res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC);
if (unlikely(!res->pub.beacon_ies))
return;
res->beacon_ies_allocated = true;
res->pub.len_beacon_ies = hidden->pub.len_beacon_ies;
memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies,
res->pub.len_beacon_ies);
}
static struct cfg80211_internal_bss * static struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *dev, cfg80211_bss_update(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *res) struct cfg80211_internal_bss *res)
@ -607,6 +703,21 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
kref_put(&res->ref, bss_release); kref_put(&res->ref, bss_release);
} else { } else {
struct cfg80211_internal_bss *hidden;
/* First check if the beacon is a probe response from
* a hidden bss. If so, copy beacon ies (with nullified
* ssid) into the probe response bss entry (with real ssid).
* It is required basically for PSM implementation
* (probe responses do not contain tim ie) */
/* TODO: The code is not trying to update existing probe
* response bss entries when beacon ies are
* getting changed. */
hidden = rb_find_hidden_bss(dev, res);
if (hidden)
copy_hidden_ies(res, hidden);
/* this "consumes" the reference */ /* this "consumes" the reference */
list_add_tail(&res->list, &dev->bss_list); list_add_tail(&res->list, &dev->bss_list);
rb_insert_bss(dev, res); rb_insert_bss(dev, res);