[PATCH 4/7] AP: Always verify and fix channel switch configuration

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux