From: Benjamin Berg <benjamin.berg@xxxxxxxxx> On the AP control interface, the CHAN_SWITCH command would do some extra verification and also ensured that HT/VHT/HE/EHT is not being disabled (as this is correctly supported currently). However, these checks were missing on the wpa_supplicant side which would be used when e.g. a CHAN_SWITCH command is done on a GO interface. Move these checks into the hostapd_parse_csa_settings function to ensure that they are correctly done in both cases. This fixes issues where for example HT is getting accidentally disabled in a test, causing a subsequent mac80211 disconnect. Some tests may occasionally trigger this problem, but usually pass due to timing (this will be addressed by a later patch). Signed-off-by: Benjamin Berg <benjamin.berg@xxxxxxxxx> Reviewed-by: Andrei Otcheretianski <andrei.otcheretianski@xxxxxxxxx> --- hostapd/ctrl_iface.c | 270 +-------------------------------------- src/ap/ctrl_iface_ap.c | 278 ++++++++++++++++++++++++++++++++++++++++- src/ap/ctrl_iface_ap.h | 3 +- wpa_supplicant/ap.c | 15 ++- 4 files changed, 293 insertions(+), 273 deletions(-) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index db6e44a61b..f3b31c79de 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -38,7 +38,6 @@ #endif /* CONFIG_DPP */ #include "common/wpa_ctrl.h" #include "common/ptksa_cache.h" -#include "common/hw_features_common.h" #include "common/nan_de.h" #include "crypto/tls.h" #include "drivers/driver.h" @@ -2429,255 +2428,11 @@ static int hostapd_ctrl_register_frame(struct hostapd_data *hapd, #endif /* CONFIG_TESTING_OPTIONS */ -#ifdef NEED_AP_MLME - -static struct hostapd_hw_modes * get_target_hw_mode(struct hostapd_iface *iface, - int freq) -{ - int i; - enum hostapd_hw_mode target_mode; - bool is_6ghz = is_6ghz_freq(freq); - - if (freq < 4000) - target_mode = HOSTAPD_MODE_IEEE80211G; - else if (freq > 50000) - target_mode = HOSTAPD_MODE_IEEE80211AD; - else - target_mode = HOSTAPD_MODE_IEEE80211A; - - for (i = 0; i < iface->num_hw_features; i++) { - struct hostapd_hw_modes *mode; - - mode = &iface->hw_features[i]; - if (mode->mode == target_mode && mode->is_6ghz == is_6ghz) - return mode; - } - - return NULL; -} - - -static bool -hostapd_ctrl_is_freq_in_mode(struct hostapd_hw_modes *mode, - struct hostapd_multi_hw_info *current_hw_info, - int freq) -{ - struct hostapd_channel_data *chan; - int i; - - for (i = 0; i < mode->num_channels; i++) { - chan = &mode->channels[i]; - - if (chan->flag & HOSTAPD_CHAN_DISABLED) - continue; - - if (!chan_in_current_hw_info(current_hw_info, chan)) - continue; - - if (chan->freq == freq) - return true; - } - return false; -} - - -static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params, - u16 punct_bitmap) -{ - u32 start_freq; - - if (is_6ghz_freq(params->freq)) { - const int bw_idx[] = { 20, 40, 80, 160, 320 }; - int idx, bw; - - /* The 6 GHz band requires HE to be enabled. */ - params->he_enabled = 1; - - if (params->center_freq1) { - if (params->freq == 5935) - idx = (params->center_freq1 - 5925) / 5; - else - idx = (params->center_freq1 - 5950) / 5; - - bw = center_idx_to_bw_6ghz(idx); - if (bw < 0 || bw >= (int) ARRAY_SIZE(bw_idx) || - bw_idx[bw] != params->bandwidth) - return -1; - } - } else { /* Non-6 GHz channel */ - /* An EHT STA is also an HE STA as defined in - * IEEE P802.11be/D5.0, 4.3.16a. */ - if (params->he_enabled || params->eht_enabled) { - params->he_enabled = 1; - /* An HE STA is also a VHT STA if operating in the 5 GHz - * band and an HE STA is also an HT STA in the 2.4 GHz - * band as defined in IEEE Std 802.11ax-2021, 4.3.15a. - * A VHT STA is an HT STA as defined in IEEE - * Std 802.11, 4.3.15. */ - if (IS_5GHZ(params->freq)) - params->vht_enabled = 1; - - params->ht_enabled = 1; - } - } - - switch (params->bandwidth) { - case 0: - /* bandwidth not specified: use 20 MHz by default */ - /* fall-through */ - case 20: - if (params->center_freq1 && - params->center_freq1 != params->freq) - return -1; - - if (params->center_freq2 || params->sec_channel_offset) - return -1; - - if (punct_bitmap) - return -1; - break; - case 40: - if (params->center_freq2 || !params->sec_channel_offset) - return -1; - - if (punct_bitmap) - return -1; - - if (!params->center_freq1) - break; - switch (params->sec_channel_offset) { - case 1: - if (params->freq + 10 != params->center_freq1) - return -1; - break; - case -1: - if (params->freq - 10 != params->center_freq1) - return -1; - break; - default: - return -1; - } - break; - case 80: - if (!params->center_freq1 || !params->sec_channel_offset) - return 1; - - switch (params->sec_channel_offset) { - case 1: - if (params->freq - 10 != params->center_freq1 && - params->freq + 30 != params->center_freq1) - return 1; - break; - case -1: - if (params->freq + 10 != params->center_freq1 && - params->freq - 30 != params->center_freq1) - return -1; - break; - default: - return -1; - } - - if (params->center_freq2 && punct_bitmap) - return -1; - - /* Adjacent and overlapped are not allowed for 80+80 */ - if (params->center_freq2 && - params->center_freq1 - params->center_freq2 <= 80 && - params->center_freq2 - params->center_freq1 <= 80) - return 1; - break; - case 160: - if (!params->center_freq1 || params->center_freq2 || - !params->sec_channel_offset) - return -1; - - switch (params->sec_channel_offset) { - case 1: - if (params->freq + 70 != params->center_freq1 && - params->freq + 30 != params->center_freq1 && - params->freq - 10 != params->center_freq1 && - params->freq - 50 != params->center_freq1) - return -1; - break; - case -1: - if (params->freq + 50 != params->center_freq1 && - params->freq + 10 != params->center_freq1 && - params->freq - 30 != params->center_freq1 && - params->freq - 70 != params->center_freq1) - return -1; - break; - default: - return -1; - } - break; - case 320: - if (!params->center_freq1 || params->center_freq2 || - !params->sec_channel_offset) - return -1; - - switch (params->sec_channel_offset) { - case 1: - if (params->freq + 150 != params->center_freq1 && - params->freq + 110 != params->center_freq1 && - params->freq + 70 != params->center_freq1 && - params->freq + 30 != params->center_freq1 && - params->freq - 10 != params->center_freq1 && - params->freq - 50 != params->center_freq1 && - params->freq - 90 != params->center_freq1 && - params->freq - 130 != params->center_freq1) - return -1; - break; - case -1: - if (params->freq + 130 != params->center_freq1 && - params->freq + 90 != params->center_freq1 && - params->freq + 50 != params->center_freq1 && - params->freq + 10 != params->center_freq1 && - params->freq - 30 != params->center_freq1 && - params->freq - 70 != params->center_freq1 && - params->freq - 110 != params->center_freq1 && - params->freq - 150 != params->center_freq1) - return -1; - break; - } - break; - default: - return -1; - } - - if (!punct_bitmap) - return 0; - - if (!params->eht_enabled) { - wpa_printf(MSG_ERROR, - "Preamble puncturing supported only in EHT"); - return -1; - } - - if (params->freq >= 2412 && params->freq <= 2484) { - wpa_printf(MSG_ERROR, - "Preamble puncturing is not supported in 2.4 GHz"); - return -1; - } - - start_freq = params->center_freq1 - (params->bandwidth / 2); - if (!is_punct_bitmap_valid(params->bandwidth, - (params->freq - start_freq) / 20, - punct_bitmap)) { - wpa_printf(MSG_ERROR, "Invalid preamble puncturing bitmap"); - return -1; - } - - return 0; -} -#endif /* NEED_AP_MLME */ - - static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface, char *pos) { #ifdef NEED_AP_MLME struct csa_settings settings; - struct hostapd_hw_modes *target_mode; int ret; int dfs_range = 0; unsigned int i; @@ -2686,7 +2441,7 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface, unsigned int num_err = 0; int err = 0; - ret = hostapd_parse_csa_settings(pos, &settings); + ret = hostapd_parse_csa_settings(iface, pos, &settings); if (ret) return ret; @@ -2696,29 +2451,6 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface, settings.link_id = iface->bss[0]->mld_link_id; #endif /* CONFIG_IEEE80211BE */ - target_mode = get_target_hw_mode(iface, settings.freq_params.freq); - if (!target_mode) { - wpa_printf(MSG_DEBUG, - "chanswitch: Invalid frequency settings provided for hw mode"); - return -1; - } - - if (iface->num_hw_features > 1 && - !hostapd_ctrl_is_freq_in_mode(target_mode, iface->current_hw_info, - settings.freq_params.freq)) { - wpa_printf(MSG_INFO, - "chanswitch: Invalid frequency settings provided for multi band phy"); - return -1; - } - - ret = hostapd_ctrl_check_freq_params(&settings.freq_params, - settings.freq_params.punct_bitmap); - if (ret) { - wpa_printf(MSG_INFO, - "chanswitch: invalid frequency settings provided"); - return ret; - } - switch (settings.freq_params.bandwidth) { case 40: bandwidth = CHAN_WIDTH_40; diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 8072710957..dcea7a0eb1 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -11,6 +11,7 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/sae.h" +#include "common/hw_features_common.h" #include "eapol_auth/eapol_auth_sm.h" #include "fst/fst_ctrl_iface.h" #include "hostapd.h" @@ -1168,10 +1169,253 @@ int hostapd_parse_freq_params(const char *pos, } -int hostapd_parse_csa_settings(const char *pos, +static struct hostapd_hw_modes * get_target_hw_mode(struct hostapd_iface *iface, + int freq) +{ + int i; + enum hostapd_hw_mode target_mode; + bool is_6ghz = is_6ghz_freq(freq); + + if (freq < 4000) + target_mode = HOSTAPD_MODE_IEEE80211G; + else if (freq > 50000) + target_mode = HOSTAPD_MODE_IEEE80211AD; + else + target_mode = HOSTAPD_MODE_IEEE80211A; + + for (i = 0; i < iface->num_hw_features; i++) { + struct hostapd_hw_modes *mode; + + mode = &iface->hw_features[i]; + if (mode->mode == target_mode && mode->is_6ghz == is_6ghz) + return mode; + } + + return NULL; +} + + +static bool +hostapd_ctrl_is_freq_in_mode(struct hostapd_hw_modes *mode, + struct hostapd_multi_hw_info *current_hw_info, + int freq) +{ + struct hostapd_channel_data *chan; + int i; + + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + if (!chan_in_current_hw_info(current_hw_info, chan)) + continue; + + if (chan->freq == freq) + return true; + } + return false; +} + + +static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params, + u16 punct_bitmap) +{ + u32 start_freq; + + if (is_6ghz_freq(params->freq)) { + const int bw_idx[] = { 20, 40, 80, 160, 320 }; + int idx, bw; + + /* The 6 GHz band requires HE to be enabled. */ + params->he_enabled = 1; + + if (params->center_freq1) { + if (params->freq == 5935) + idx = (params->center_freq1 - 5925) / 5; + else + idx = (params->center_freq1 - 5950) / 5; + + bw = center_idx_to_bw_6ghz(idx); + if (bw < 0 || bw >= (int) ARRAY_SIZE(bw_idx) || + bw_idx[bw] != params->bandwidth) + return -1; + } + } else { /* Non-6 GHz channel */ + /* An EHT STA is also an HE STA as defined in + * IEEE P802.11be/D5.0, 4.3.16a. */ + if (params->he_enabled || params->eht_enabled) { + params->he_enabled = 1; + /* An HE STA is also a VHT STA if operating in the 5 GHz + * band and an HE STA is also an HT STA in the 2.4 GHz + * band as defined in IEEE Std 802.11ax-2021, 4.3.15a. + * A VHT STA is an HT STA as defined in IEEE + * Std 802.11, 4.3.15. */ + if (IS_5GHZ(params->freq)) + params->vht_enabled = 1; + + params->ht_enabled = 1; + } + } + + switch (params->bandwidth) { + case 0: + /* bandwidth not specified: use 20 MHz by default */ + /* fall-through */ + case 20: + if (params->center_freq1 && + params->center_freq1 != params->freq) + return -1; + + if (params->center_freq2 || params->sec_channel_offset) + return -1; + + if (punct_bitmap) + return -1; + break; + case 40: + if (params->center_freq2 || !params->sec_channel_offset) + return -1; + + if (punct_bitmap) + return -1; + + if (!params->center_freq1) + break; + switch (params->sec_channel_offset) { + case 1: + if (params->freq + 10 != params->center_freq1) + return -1; + break; + case -1: + if (params->freq - 10 != params->center_freq1) + return -1; + break; + default: + return -1; + } + break; + case 80: + if (!params->center_freq1 || !params->sec_channel_offset) + return 1; + + switch (params->sec_channel_offset) { + case 1: + if (params->freq - 10 != params->center_freq1 && + params->freq + 30 != params->center_freq1) + return 1; + break; + case -1: + if (params->freq + 10 != params->center_freq1 && + params->freq - 30 != params->center_freq1) + return -1; + break; + default: + return -1; + } + + if (params->center_freq2 && punct_bitmap) + return -1; + + /* Adjacent and overlapped are not allowed for 80+80 */ + if (params->center_freq2 && + params->center_freq1 - params->center_freq2 <= 80 && + params->center_freq2 - params->center_freq1 <= 80) + return 1; + break; + case 160: + if (!params->center_freq1 || params->center_freq2 || + !params->sec_channel_offset) + return -1; + + switch (params->sec_channel_offset) { + case 1: + if (params->freq + 70 != params->center_freq1 && + params->freq + 30 != params->center_freq1 && + params->freq - 10 != params->center_freq1 && + params->freq - 50 != params->center_freq1) + return -1; + break; + case -1: + if (params->freq + 50 != params->center_freq1 && + params->freq + 10 != params->center_freq1 && + params->freq - 30 != params->center_freq1 && + params->freq - 70 != params->center_freq1) + return -1; + break; + default: + return -1; + } + break; + case 320: + if (!params->center_freq1 || params->center_freq2 || + !params->sec_channel_offset) + return -1; + + switch (params->sec_channel_offset) { + case 1: + if (params->freq + 150 != params->center_freq1 && + params->freq + 110 != params->center_freq1 && + params->freq + 70 != params->center_freq1 && + params->freq + 30 != params->center_freq1 && + params->freq - 10 != params->center_freq1 && + params->freq - 50 != params->center_freq1 && + params->freq - 90 != params->center_freq1 && + params->freq - 130 != params->center_freq1) + return -1; + break; + case -1: + if (params->freq + 130 != params->center_freq1 && + params->freq + 90 != params->center_freq1 && + params->freq + 50 != params->center_freq1 && + params->freq + 10 != params->center_freq1 && + params->freq - 30 != params->center_freq1 && + params->freq - 70 != params->center_freq1 && + params->freq - 110 != params->center_freq1 && + params->freq - 150 != params->center_freq1) + return -1; + break; + } + break; + default: + return -1; + } + + if (!punct_bitmap) + return 0; + + if (!params->eht_enabled) { + wpa_printf(MSG_ERROR, + "Preamble puncturing supported only in EHT"); + return -1; + } + + if (params->freq >= 2412 && params->freq <= 2484) { + wpa_printf(MSG_ERROR, + "Preamble puncturing is not supported in 2.4 GHz"); + return -1; + } + + start_freq = params->center_freq1 - (params->bandwidth / 2); + if (!is_punct_bitmap_valid(params->bandwidth, + (params->freq - start_freq) / 20, + punct_bitmap)) { + wpa_printf(MSG_ERROR, "Invalid preamble puncturing bitmap"); + return -1; + } + + return 0; +} + + +int hostapd_parse_csa_settings(struct hostapd_iface *iface, + const char *pos, struct csa_settings *settings) { + struct hostapd_hw_modes *target_mode; char *end; + int ret; os_memset(settings, 0, sizeof(*settings)); settings->cs_count = strtol(pos, &end, 10); @@ -1182,7 +1426,37 @@ int hostapd_parse_csa_settings(const char *pos, settings->block_tx = !!os_strstr(pos, " blocktx"); - return hostapd_parse_freq_params(end, &settings->freq_params, 0); + ret = hostapd_parse_freq_params(end, &settings->freq_params, 0); + if (ret < 0) { + wpa_printf(MSG_INFO, + "chanswitch: failed to parse frequency parameters"); + return ret; + } + + target_mode = get_target_hw_mode(iface, settings->freq_params.freq); + if (!target_mode) { + wpa_printf(MSG_DEBUG, + "chanswitch: Invalid frequency settings provided for hw mode"); + return -1; + } + + if (iface->num_hw_features > 1 && + !hostapd_ctrl_is_freq_in_mode(target_mode, iface->current_hw_info, + settings->freq_params.freq)) { + wpa_printf(MSG_INFO, + "chanswitch: Invalid frequency settings provided for multi band phy"); + return -1; + } + + ret = hostapd_ctrl_check_freq_params(&settings->freq_params, + settings->freq_params.punct_bitmap); + if (ret) { + wpa_printf(MSG_INFO, + "chanswitch: invalid frequency settings provided"); + return ret; + } + + return 0; } diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h index 926a51f878..4de140844f 100644 --- a/src/ap/ctrl_iface_ap.h +++ b/src/ap/ctrl_iface_ap.h @@ -29,7 +29,8 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, int hostapd_parse_freq_params(const char *pos, struct hostapd_freq_params *params, unsigned int freq); -int hostapd_parse_csa_settings(const char *pos, +int hostapd_parse_csa_settings(struct hostapd_iface *iface, + const char *pos, struct csa_settings *settings); int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd); int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index de18a68f0b..bf68d4425a 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -1852,8 +1852,21 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s, int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) { struct csa_settings settings; - int ret = hostapd_parse_csa_settings(pos, &settings); + struct hostapd_iface *iface = NULL; + int ret; + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) + iface = wpa_s->ap_iface; + else +#endif + if (wpa_s->ifmsh) + iface = wpa_s->ifmsh; + + if (!iface) + return -1; + ret = hostapd_parse_csa_settings(iface, pos, &settings); if (ret) return ret; -- 2.50.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap