Commit 04a161f4 authored by Johannes Berg's avatar Johannes Berg

mac80211: fix HT beacon-based channel switch handling

When an HT AP is advertising channel switch in a beacon, it
doesn't (and shouldn't, according to 802.11-2012 Table 8-20)
include a secondary channel offset element. The only possible
interpretation is that the previous secondary channel offset
remains valid, so use that when switching channel based only
on beacon information.

VHT requires the Wide Bandwidth Channel Switch subelement to
be present in the Channel Switch Wrapper element, so the code
for that is probably ok (see 802.11ac Draft 4, 8.4.2.165.)
Reported-by: default avatarSujith Manoharan <c_manoha@qca.qualcomm.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 03f831a6
...@@ -1015,7 +1015,8 @@ static void ieee80211_chswitch_timer(unsigned long data) ...@@ -1015,7 +1015,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
static void static void
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
u64 timestamp, struct ieee802_11_elems *elems) u64 timestamp, struct ieee802_11_elems *elems,
bool beacon)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
...@@ -1032,6 +1033,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, ...@@ -1032,6 +1033,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct cfg80211_chan_def new_vht_chandef = {}; struct cfg80211_chan_def new_vht_chandef = {};
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
const struct ieee80211_ht_operation *ht_oper;
int secondary_channel_offset = -1; int secondary_channel_offset = -1;
ASSERT_MGD_MTX(ifmgd); ASSERT_MGD_MTX(ifmgd);
...@@ -1048,11 +1050,14 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, ...@@ -1048,11 +1050,14 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
sec_chan_offs = elems->sec_chan_offs; sec_chan_offs = elems->sec_chan_offs;
wide_bw_chansw_ie = elems->wide_bw_chansw_ie; wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
ht_oper = elems->ht_operation;
if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
IEEE80211_STA_DISABLE_40MHZ)) { IEEE80211_STA_DISABLE_40MHZ)) {
sec_chan_offs = NULL; sec_chan_offs = NULL;
wide_bw_chansw_ie = NULL; wide_bw_chansw_ie = NULL;
/* only used for bandwidth here */
ht_oper = NULL;
} }
if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
...@@ -1094,10 +1099,20 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, ...@@ -1094,10 +1099,20 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
return; return;
} }
if (sec_chan_offs) { if (!beacon && sec_chan_offs) {
secondary_channel_offset = sec_chan_offs->sec_chan_offs; secondary_channel_offset = sec_chan_offs->sec_chan_offs;
} else if (beacon && ht_oper) {
secondary_channel_offset =
ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
} else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { } else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
/* if HT is enabled and the IE not present, it's still HT */ /*
* If it's not a beacon, HT is enabled and the IE not present,
* it's 20 MHz, 802.11-2012 8.5.2.6:
* This element [the Secondary Channel Offset Element] is
* present when switching to a 40 MHz channel. It may be
* present when switching to a 20 MHz channel (in which
* case the secondary channel offset is set to SCN).
*/
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
} }
...@@ -2796,7 +2811,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -2796,7 +2811,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&local->iflist_mtx); mutex_unlock(&local->iflist_mtx);
} }
ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, elems); ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
elems, true);
} }
...@@ -3210,7 +3226,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ...@@ -3210,7 +3226,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_process_chanswitch(sdata, ieee80211_sta_process_chanswitch(sdata,
rx_status->mactime, rx_status->mactime,
&elems); &elems, false);
} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
ies_len = skb->len - ies_len = skb->len -
offsetof(struct ieee80211_mgmt, offsetof(struct ieee80211_mgmt,
...@@ -3232,7 +3248,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ...@@ -3232,7 +3248,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_process_chanswitch(sdata, ieee80211_sta_process_chanswitch(sdata,
rx_status->mactime, rx_status->mactime,
&elems); &elems, false);
} }
break; break;
} }
......
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