Commit 826262c3 authored by Johannes Berg's avatar Johannes Berg

mac80211: fix dtim_period in hidden SSID AP association

When AP's SSID is hidden the BSS can appear several times in
cfg80211's BSS list: once with a zero-length SSID that comes
from the beacon, and once for each SSID from probe reponses.

Since the mac80211 stores its data in ieee80211_bss which
is embedded into cfg80211_bss, mac80211's data will be
duplicated too.

This becomes a problem when a driver needs the dtim_period
since this data exists only in the beacon's instance in
cfg80211 bss table which isn't the instance that is used
when associating.

Remove the DTIM period from the BSS table and track it
explicitly to avoid this problem.

Cc: stable@vger.kernel.org
Tested-by: default avatarEfi Tubul <efi.tubul@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent a56f992c
...@@ -92,8 +92,6 @@ struct ieee80211_bss { ...@@ -92,8 +92,6 @@ struct ieee80211_bss {
u32 device_ts; u32 device_ts;
u8 dtim_period;
bool wmm_used; bool wmm_used;
bool uapsd_supported; bool uapsd_supported;
...@@ -140,7 +138,6 @@ enum ieee80211_bss_corrupt_data_flags { ...@@ -140,7 +138,6 @@ enum ieee80211_bss_corrupt_data_flags {
/** /**
* enum ieee80211_valid_data_flags - BSS valid data flags * enum ieee80211_valid_data_flags - BSS valid data flags
* @IEEE80211_BSS_VALID_DTIM: DTIM data was gathered from non-corrupt IE
* @IEEE80211_BSS_VALID_WMM: WMM/UAPSD data was gathered from non-corrupt IE * @IEEE80211_BSS_VALID_WMM: WMM/UAPSD data was gathered from non-corrupt IE
* @IEEE80211_BSS_VALID_RATES: Supported rates were gathered from non-corrupt IE * @IEEE80211_BSS_VALID_RATES: Supported rates were gathered from non-corrupt IE
* @IEEE80211_BSS_VALID_ERP: ERP flag was gathered from non-corrupt IE * @IEEE80211_BSS_VALID_ERP: ERP flag was gathered from non-corrupt IE
...@@ -151,7 +148,6 @@ enum ieee80211_bss_corrupt_data_flags { ...@@ -151,7 +148,6 @@ enum ieee80211_bss_corrupt_data_flags {
* beacon/probe response. * beacon/probe response.
*/ */
enum ieee80211_bss_valid_data_flags { enum ieee80211_bss_valid_data_flags {
IEEE80211_BSS_VALID_DTIM = BIT(0),
IEEE80211_BSS_VALID_WMM = BIT(1), IEEE80211_BSS_VALID_WMM = BIT(1),
IEEE80211_BSS_VALID_RATES = BIT(2), IEEE80211_BSS_VALID_RATES = BIT(2),
IEEE80211_BSS_VALID_ERP = BIT(3) IEEE80211_BSS_VALID_ERP = BIT(3)
...@@ -440,6 +436,7 @@ struct ieee80211_if_managed { ...@@ -440,6 +436,7 @@ struct ieee80211_if_managed {
unsigned long timers_running; /* used for quiesce/restart */ unsigned long timers_running; /* used for quiesce/restart */
bool powersave; /* powersave requested for this iface */ bool powersave; /* powersave requested for this iface */
bool broken_ap; /* AP is broken -- turn off powersave */ bool broken_ap; /* AP is broken -- turn off powersave */
u8 dtim_period;
enum ieee80211_smps_mode req_smps, /* requested smps mode */ enum ieee80211_smps_mode req_smps, /* requested smps mode */
driver_smps_mode; /* smps mode request */ driver_smps_mode; /* smps mode request */
......
...@@ -1074,12 +1074,8 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) ...@@ -1074,12 +1074,8 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
if (beaconint_us > latency) { if (beaconint_us > latency) {
local->ps_sdata = NULL; local->ps_sdata = NULL;
} else { } else {
struct ieee80211_bss *bss;
int maxslp = 1; int maxslp = 1;
u8 dtimper; u8 dtimper = found->u.mgd.dtim_period;
bss = (void *)found->u.mgd.associated->priv;
dtimper = bss->dtim_period;
/* If the TIM IE is invalid, pretend the value is 1 */ /* If the TIM IE is invalid, pretend the value is 1 */
if (!dtimper) if (!dtimper)
...@@ -1410,10 +1406,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ...@@ -1410,10 +1406,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ieee80211_led_assoc(local, 1); ieee80211_led_assoc(local, 1);
if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
bss_conf->dtim_period = bss->dtim_period; /*
else * If the AP is buggy we may get here with no DTIM period
* known, so assume it's 1 which is the only safe assumption
* in that case, although if the TIM IE is broken powersave
* probably just won't work at all.
*/
bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
} else {
bss_conf->dtim_period = 0; bss_conf->dtim_period = 0;
}
bss_conf->assoc = 1; bss_conf->assoc = 1;
...@@ -1562,6 +1565,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ...@@ -1562,6 +1565,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.timers_running = 0; sdata->u.mgd.timers_running = 0;
sdata->vif.bss_conf.dtim_period = 0;
ifmgd->flags = 0; ifmgd->flags = 0;
ieee80211_vif_release_channel(sdata); ieee80211_vif_release_channel(sdata);
} }
...@@ -2373,11 +2378,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -2373,11 +2378,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel; struct ieee80211_channel *channel;
bool need_ps = false; bool need_ps = false;
if (sdata->u.mgd.associated && if ((sdata->u.mgd.associated &&
ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) { ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||
bss = (void *)sdata->u.mgd.associated->priv; (sdata->u.mgd.assoc_data &&
ether_addr_equal(mgmt->bssid,
sdata->u.mgd.assoc_data->bss->bssid))) {
/* not previously set so we may need to recalc */ /* not previously set so we may need to recalc */
need_ps = !bss->dtim_period; need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period;
if (elems->tim && !elems->parse_error) {
struct ieee80211_tim_ie *tim_ie = elems->tim;
sdata->u.mgd.dtim_period = tim_ie->dtim_period;
}
} }
if (elems->ds_params && elems->ds_params_len == 1) if (elems->ds_params && elems->ds_params_len == 1)
...@@ -3896,20 +3908,41 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ...@@ -3896,20 +3908,41 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
/* kick off associate process */ /* kick off associate process */
ifmgd->assoc_data = assoc_data; ifmgd->assoc_data = assoc_data;
ifmgd->dtim_period = 0;
err = ieee80211_prep_connection(sdata, req->bss, true); err = ieee80211_prep_connection(sdata, req->bss, true);
if (err) if (err)
goto err_clear; goto err_clear;
if (!bss->dtim_period && if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) { const struct cfg80211_bss_ies *beacon_ies;
rcu_read_lock();
beacon_ies = rcu_dereference(req->bss->beacon_ies);
if (!beacon_ies) {
/* /*
* Wait up to one beacon interval ... * Wait up to one beacon interval ...
* should this be more if we miss one? * should this be more if we miss one?
*/ */
sdata_info(sdata, "waiting for beacon from %pM\n", sdata_info(sdata, "waiting for beacon from %pM\n",
ifmgd->bssid); ifmgd->bssid);
assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); assoc_data->timeout =
TU_TO_EXP_TIME(req->bss->beacon_interval);
} else {
const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
beacon_ies->data,
beacon_ies->len);
if (tim_ie && tim_ie[1] >=
sizeof(struct ieee80211_tim_ie)) {
const struct ieee80211_tim_ie *tim;
tim = (void *)(tim_ie + 2);
ifmgd->dtim_period = tim->dtim_period;
}
assoc_data->have_beacon = true;
assoc_data->sent_assoc = false;
assoc_data->timeout = jiffies;
}
rcu_read_unlock();
} else { } else {
assoc_data->have_beacon = true; assoc_data->have_beacon = true;
assoc_data->sent_assoc = false; assoc_data->sent_assoc = false;
......
...@@ -113,18 +113,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local, ...@@ -113,18 +113,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
bss->valid_data |= IEEE80211_BSS_VALID_ERP; bss->valid_data |= IEEE80211_BSS_VALID_ERP;
} }
if (elems->tim && (!elems->parse_error ||
!(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) {
struct ieee80211_tim_ie *tim_ie = elems->tim;
bss->dtim_period = tim_ie->dtim_period;
if (!elems->parse_error)
bss->valid_data |= IEEE80211_BSS_VALID_DTIM;
}
/* If the beacon had no TIM IE, or it was invalid, use 1 */
if (beacon && !bss->dtim_period)
bss->dtim_period = 1;
/* replace old supported rates if we get new values */ /* replace old supported rates if we get new values */
if (!elems->parse_error || if (!elems->parse_error ||
!(bss->valid_data & IEEE80211_BSS_VALID_RATES)) { !(bss->valid_data & IEEE80211_BSS_VALID_RATES)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment