[PATCH 4/7] AP: Always verify and fix channel switch configuration
Benjamin Berg
benjamin at sipsolutions.net
Thu Jul 31 06:57:00 PDT 2025
From: Benjamin Berg <benjamin.berg at intel.com>
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 at intel.com>
Reviewed-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
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
More information about the Hostap
mailing list