[PATCH RFC 6/9] AP: add S1G support
Lachlan Hodges
lachlan.hodges at morsemicro.com
Thu Oct 8 20:32:03 PDT 2026
Introduce the ability to bring up an S1G AP. Currently only US 2024
channelisation is supported. A sample config is as follows:
```
interface=wlan0
country_code=US
driver=nl80211
ssid=wifi_halow
channel=43
ieee80211ah=1
op_class=71
beacon_int=100
dtim_period=10
s1g_oper_centr_freq_idx=44
s1g_primary_2mhz=1
hw_mode=ah
wpa=2
wpa_key_mgmt=SAE
rsn_pairwise=CCMP
sae_password=12345678
ieee80211w=2
```
Where we are configuring an operating channel of 44, primary 1MHz
channel of 43, whilst utilising a 2MHz primary. There is no need to
include sae_pwe=1 as H2E will automatically be enabled for S1G.
ieee80211w=2 is required, and will error if not set correctly during
config validation.
Signed-off-by: Lachlan Hodges <lachlan.hodges at morsemicro.com>
---
hostapd/Makefile | 5 +
hostapd/config_file.c | 12 ++
src/ap/Makefile | 2 +
src/ap/ap_config.c | 54 +++++++++
src/ap/ap_config.h | 23 ++++
src/ap/ap_drv_ops.c | 8 ++
src/ap/ap_drv_ops.h | 4 +-
src/ap/beacon.c | 92 +++++++++++++--
src/ap/ctrl_iface_ap.c | 8 +-
src/ap/dfs.c | 2 +-
src/ap/hostapd.c | 7 +-
src/ap/hw_features.c | 31 ++++-
src/ap/ieee802_11.c | 55 +++++++--
src/ap/ieee802_11.h | 6 +
src/ap/ieee802_11_s1g.c | 202 ++++++++++++++++++++++++++++++++
src/ap/interference.c | 1 +
src/ap/sta_info.c | 3 +-
src/ap/sta_info.h | 2 +
src/common/defs.h | 6 +
src/common/hw_features_common.c | 46 +++++++-
src/common/hw_features_common.h | 7 ++
src/common/ieee802_11_common.c | 99 +++++++++++++++-
src/common/ieee802_11_common.h | 6 +-
src/common/ieee802_11_defs.h | 91 +++++++++++---
src/drivers/driver.h | 24 +++-
wlantest/bip.c | 12 +-
wlantest/bss.c | 2 +-
27 files changed, 752 insertions(+), 58 deletions(-)
create mode 100644 src/ap/ieee802_11_s1g.c
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 1ecafdc5591e..7fb18f86882d 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -369,6 +369,11 @@ NEED_JSON=y
endif
endif
+ifdef CONFIG_IEEE80211AH
+CFLAGS += -DCONFIG_IEEE80211AH
+OBJS += ../src/ap/ieee802_11_s1g.o
+endif
+
ifdef CONFIG_MBO
CFLAGS += -DCONFIG_MBO
OBJS += ../src/ap/mbo_ap.o
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 2717adc55bd5..232c415aa1d4 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -3345,6 +3345,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
else if (os_strcmp(pos, "ad") == 0)
conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
+#ifdef CONFIG_IEEE80211AH
+ else if (os_strcmp(pos, "ah") == 0)
+ conf->hw_mode = HOSTAPD_MODE_IEEE80211AH;
+#endif /* CONFIG_IEEE80211AH */
else if (os_strcmp(pos, "any") == 0)
conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY;
else {
@@ -4004,6 +4008,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "mbssid_max") == 0) {
conf->mbssid_max = atoi(pos);
#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211AH
+ } else if (os_strcmp(buf, "ieee80211ah") == 0) {
+ conf->ieee80211ah = atoi(pos);
+ } else if (os_strcmp(buf, "s1g_oper_centr_freq_idx") == 0) {
+ conf->s1g_oper_centr_freq_idx = atoi(pos);
+ } else if (os_strcmp(buf, "s1g_primary_2mhz") == 0) {
+ conf->s1g_primary_2mhz = atoi(pos);
+#endif /* CONFIG_IEEE80211AH */
} else if (os_strcmp(buf, "max_listen_interval") == 0) {
bss->max_listen_interval = atoi(pos);
} else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 856c3db5c18a..9f0a4d2452bf 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -7,6 +7,7 @@ CFLAGS += -DCONFIG_IEEE80211AC
CFLAGS += -DCONFIG_IEEE80211AX
CFLAGS += -DCONFIG_IEEE80211BE
CFLAGS += -DCONFIG_IEEE80211BN
+CFLAGS += -DCONFIG_IEEE80211AH
CFLAGS += -DCONFIG_IEEE80211R
CFLAGS += -DCONFIG_IEEE80211R_AP
CFLAGS += -DCONFIG_WPS
@@ -43,6 +44,7 @@ LIB_OBJS= \
ieee802_11_shared.o \
ieee802_11_uhr.o \
ieee802_11_vht.o \
+ ieee802_11_s1g.o \
ieee802_1x.o \
interference.o \
neighbor_db.o \
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 95a982f01c3e..65b8f09a0512 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1230,6 +1230,26 @@ static bool hostapd_sae_pk_password_without_pk(struct hostapd_bss_config *bss)
}
#endif /* CONFIG_SAE_PK */
+#ifdef CONFIG_IEEE80211AH
+static bool hostapd_config_check_bss_s1g(struct hostapd_bss_config *bss)
+{
+ if (bss->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED) {
+ wpa_printf(MSG_ERROR,
+ "Management frame protection is required in S1GHz (ieee80211w=2)");
+ return false;
+ }
+
+#ifdef CONFIG_SAE
+ if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) &&
+ bss->sae_pwe != SAE_PWE_HASH_TO_ELEMENT) {
+ wpa_printf(MSG_INFO, "SAE: Forcing SAE H2E on S1GHz");
+ bss->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+ }
+#endif /* CONFIG_SAE */
+
+ return true;
+}
+#endif /* CONFIG_IEEE80211AH */
bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss)
{
@@ -1307,6 +1327,12 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
!hostapd_config_check_bss_6g(bss))
return -1;
+#ifdef CONFIG_IEEE80211AH
+ if (full_config && conf->hw_mode == HOSTAPD_MODE_IEEE80211AH &&
+ !hostapd_config_check_bss_s1g(bss))
+ return -1;
+#endif /* CONFIG_IEEE80211AH */
+
if (full_config && bss->ieee802_1x && !bss->eap_server &&
!bss->radius->auth_servers) {
wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no "
@@ -1700,6 +1726,34 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config)
return -1;
}
+#ifdef CONFIG_IEEE80211AH
+ if (full_config && conf->ieee80211ah &&
+ conf->hw_mode != HOSTAPD_MODE_IEEE80211AH) {
+ wpa_printf(MSG_ERROR, "ieee80211ah=1 requires hw_mode=ah");
+ return -1;
+ }
+
+ if (full_config && conf->hw_mode == HOSTAPD_MODE_IEEE80211AH &&
+ !conf->ieee80211ah) {
+ wpa_printf(MSG_ERROR, "hw_mode=ah requires ieee80211ah=1");
+ return -1;
+ }
+
+ if (full_config && conf->hw_mode == HOSTAPD_MODE_IEEE80211AH &&
+ !global_op_class_to_s1g_op_class(conf->op_class)) {
+ wpa_printf(MSG_ERROR,
+ "Unsupported op_class %u for S1G operation",
+ conf->op_class);
+ return -1;
+ }
+
+ if (full_config && conf->hw_mode == HOSTAPD_MODE_IEEE80211AH &&
+ conf->acs) {
+ wpa_printf(MSG_ERROR, "ACS is not supported for S1G");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211AH */
+
for (i = 0; i < conf->num_bss; i++) {
if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
return -1;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index e3dcbfd82427..b79a281567af 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1101,6 +1101,7 @@ struct hostapd_config {
u8 channel;
int enable_edmg;
u8 edmg_channel;
+ bool s1g_primary_2mhz;
u8 acs;
struct wpa_freq_range_list acs_ch_list;
struct wpa_freq_range_list acs_freq_list;
@@ -1231,6 +1232,12 @@ struct hostapd_config {
bool require_he;
#endif /* CONFIG_IEEE80211AX */
+ int ieee80211ah;
+#ifdef CONFIG_IEEE80211AH
+ int s1g_oper_centr_freq_idx;
+ enum oper_chan_width s1g_oper_chwidth;
+#endif /* CONFIG_IEEE80211AH */
+
/* VHT enable/disable config from CHAN_SWITCH */
#define CH_SWITCH_VHT_ENABLED BIT(0)
#define CH_SWITCH_VHT_DISABLED BIT(1)
@@ -1346,6 +1353,10 @@ struct hostapd_config {
static inline enum oper_chan_width
hostapd_get_oper_chwidth(struct hostapd_config *conf)
{
+#ifdef CONFIG_IEEE80211AH
+ if (conf->ieee80211ah)
+ return conf->s1g_oper_chwidth;
+#endif /* CONFIG_IEEE80211AH */
#ifdef CONFIG_IEEE80211BE
if (conf->ieee80211be)
return conf->eht_oper_chwidth;
@@ -1361,6 +1372,10 @@ static inline void
hostapd_set_oper_chwidth(struct hostapd_config *conf,
enum oper_chan_width oper_chwidth)
{
+#ifdef CONFIG_IEEE80211AH
+ if (conf->ieee80211ah)
+ conf->s1g_oper_chwidth = oper_chwidth;
+#endif /* CONFIG_IEEE80211AH */
#ifdef CONFIG_IEEE80211BE
if (conf->ieee80211be)
conf->eht_oper_chwidth = oper_chwidth;
@@ -1377,6 +1392,10 @@ hostapd_set_oper_chwidth(struct hostapd_config *conf,
static inline u8
hostapd_get_oper_centr_freq_seg0_idx(struct hostapd_config *conf)
{
+#ifdef CONFIG_IEEE80211AH
+ if (conf->ieee80211ah)
+ return conf->s1g_oper_centr_freq_idx;
+#endif /* CONFIG_IEEE80211AH */
#ifdef CONFIG_IEEE80211BE
if (conf->ieee80211be)
return conf->eht_oper_centr_freq_seg0_idx;
@@ -1392,6 +1411,10 @@ static inline void
hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf,
u8 oper_centr_freq_seg0_idx)
{
+#ifdef CONFIG_IEEE80211AH
+ if (conf->ieee80211ah)
+ conf->s1g_oper_centr_freq_idx = oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211AH */
#ifdef CONFIG_IEEE80211BE
if (conf->ieee80211be)
conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 90d2909cbe90..2f9a31e6513b 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -506,6 +506,7 @@ int hostapd_sta_add(struct hostapd_data *hapd,
const struct ieee80211_uhr_capabilities *uhr_capab,
size_t uhr_capab_len,
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+ const struct ieee80211_s1g_capabilities *s1g_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set, const u8 *link_addr, bool mld_link_sta,
u16 eml_cap, bool epp_sta)
@@ -533,6 +534,7 @@ int hostapd_sta_add(struct hostapd_data *hapd,
params.uhr_capab = uhr_capab;
params.uhr_capab_len = uhr_capab_len;
params.he_6ghz_capab = he_6ghz_capab;
+ params.s1g_capab = s1g_capab;
params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
params.vht_opmode = vht_opmode;
params.flags = hostapd_sta_flags_to_drv(flags);
@@ -680,6 +682,7 @@ int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
int edmg, u8 edmg_channel,
int ht_enabled, int vht_enabled,
int he_enabled, bool eht_enabled,
+ int s1g_enabled,
int sec_channel_offset, int oper_chwidth,
int center_segment0, int center_segment1)
{
@@ -704,6 +707,9 @@ int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
.he.cap = cmode ? &cmode->he_capab[IEEE80211_MODE_AP] : NULL,
.eht.cap = cmode ? &cmode->eht_capab[IEEE80211_MODE_AP] : NULL,
.eht.punct_bitmap = hostapd_get_punct_bitmap(hapd),
+ .s1g.enabled = s1g_enabled,
+ .s1g.op_class = hapd->iconf->op_class,
+ .s1g.primary_2mhz = hapd->iconf->s1g_primary_2mhz,
};
if (hostapd_set_freq_params(&data, &info))
@@ -1302,6 +1308,8 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd)
for (i = 0; i < hapd->iface->num_hw_features; i++) {
mode = &hapd->iface->hw_features[i];
+ if (mode->mode == HOSTAPD_MODE_IEEE80211AH)
+ continue;
if (selected_mode != HOSTAPD_MODE_IEEE80211ANY &&
selected_mode != mode->mode)
continue;
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 972514d38f72..593ecae7260d 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -50,6 +50,7 @@ int hostapd_sta_add(struct hostapd_data *hapd,
const struct ieee80211_uhr_capabilities *uhr_capab,
size_t uhr_capab_len,
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+ const struct ieee80211_s1g_capabilities *s1g_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set, const u8 *link_addr, bool mld_link_sta,
u16 eml_cap, bool epp_sta);
@@ -76,7 +77,8 @@ int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
int freq, int freq_offset,
int channel, int edmg, u8 edmg_channel,
int ht_enabled, int vht_enabled, int he_enabled,
- bool eht_enabled, int sec_channel_offset, int oper_chwidth,
+ bool eht_enabled, int s1g_enabled,
+ int sec_channel_offset, int oper_chwidth,
int center_segment0, int center_segment1);
int hostapd_set_rts(struct hostapd_data *hapd, int rts);
int hostapd_set_frag(struct hostapd_data *hapd, int frag);
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 3a886e00544b..2df4cd35809f 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -837,6 +837,12 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
}
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211AH
+ if (hapd->iconf->ieee80211ah)
+ buflen += 2 + sizeof(struct ieee80211_s1g_capabilities) +
+ 2 + sizeof(struct ieee80211_s1g_operation);
+#endif /* CONFIG_IEEE80211AH */
+
buflen += hostapd_eid_mbssid_len(hapd_probed, WLAN_FC_STYPE_PROBE_RESP,
NULL,
params->known_bss,
@@ -956,6 +962,13 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
}
#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AH
+ if (hapd->iconf->ieee80211ah) {
+ pos = hostapd_eid_s1g_capab(hapd, pos);
+ pos = hostapd_eid_s1g_oper(hapd, pos);
+ }
+#endif /* CONFIG_IEEE80211AH */
+
#ifdef CONFIG_IEEE80211AX
if (hostapd_is_he_enabled(hapd) &&
is_6ghz_op_class(hapd->iconf->op_class))
@@ -1554,7 +1567,13 @@ void handle_probe_req(struct hostapd_data *hapd,
return;
}
- if ((!elems.ssid || !elems.supp_rates)) {
+ /*
+ * S1G does not support legacy rates. The equivalent information is
+ * advertised in the S1G Capabilities element.
+ */
+ if (!elems.ssid ||
+ (!elems.supp_rates &&
+ !(hapd->iconf->ieee80211ah && elems.s1g_capabilities))) {
wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
"without SSID or supported rates element",
MAC2STR(mgmt->sa));
@@ -2349,6 +2368,14 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
}
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211AH
+ if (hapd->iconf->ieee80211ah) {
+ /* +2 for element type + length respectively */
+ tail_len += 2 + sizeof(struct ieee80211_s1g_operation) +
+ 2 + sizeof(struct ieee80211_s1g_capabilities);
+ }
+#endif /* CONFIG_IEEE80211AH */
+
if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
hapd == hostapd_mbssid_get_tx_bss(hapd))
tail_len += 5; /* Multiple BSSID Configuration element */
@@ -2375,20 +2402,54 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
}
tailend = tail + tail_len;
- head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_BEACON);
- head->duration = host_to_le16(0);
- os_memset(head->da, 0xff, ETH_ALEN);
+ if (hapd->iconf->ieee80211ah) {
+ /* Same buffer as @head, just a different frame format */
+ struct ieee80211_ext *ext_head = (struct ieee80211_ext *) head;
+
+ ext_head->frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_EXT,
+ WLAN_FC_STYPE_S1G_BEACON) |
+ host_to_le16(WLAN_S1G_FC_BSS_BW(
+ WLAN_S1G_BSS_BW_MIN_1_MAX_16));
+ if (hostapd_own_capab_info(hapd) & WLAN_CAPABILITY_PRIVACY)
+ ext_head->frame_control |=
+ host_to_le16(WLAN_FC_S1G_BEACON_SECURITY);
+ ext_head->duration = host_to_le16(0);
+ os_memcpy(ext_head->sa, hapd->own_addr, ETH_ALEN);
+ pos = &ext_head->u.beacon.variable[0];
+ } else {
+ head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_BEACON);
+ head->duration = host_to_le16(0);
+ os_memset(head->da, 0xff, ETH_ALEN);
- os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
- head->u.beacon.beacon_int =
- host_to_le16(hapd->iconf->beacon_int);
+ os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+ head->u.beacon.beacon_int =
+ host_to_le16(hapd->iconf->beacon_int);
- /* hardware or low-level driver will setup seq_ctrl and timestamp */
- capab_info = hostapd_own_capab_info(hapd);
- head->u.beacon.capab_info = host_to_le16(capab_info);
- pos = &head->u.beacon.variable[0];
+ /* hardware or low-level driver will setup seq_ctrl and
+ * timestamp */
+ capab_info = hostapd_own_capab_info(hapd);
+ head->u.beacon.capab_info = host_to_le16(capab_info);
+
+ pos = &head->u.beacon.variable[0];
+ }
+
+ /*
+ * As per IEEE80211-2024 11.1.3.10.1 the first element in a beacon
+ * transmitted at a TBTT that is not a TSBTT (i.e a long beacon) shall
+ * contain the Beacon Compatibility element as the first element in a
+ * beacon. Therefore insert this at the head before the SSID element.
+ * Other elements can be inserted in the tail as normal.
+ */
+#ifdef CONFIG_IEEE80211AH
+ if (hapd->iconf->ieee80211ah) {
+ pos = hostapd_eid_s1g_beacon_compat(hapd, pos);
+ tailpos = hostapd_eid_s1g_capab(hapd, tailpos);
+ tailpos = hostapd_eid_s1g_oper(hapd, tailpos);
+ }
+#endif /* CONFIG_IEEE80211AH */
/* SSID */
*pos++ = WLAN_EID_SSID;
@@ -2910,6 +2971,9 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
.he.cap = cmode ? &cmode->he_capab[IEEE80211_MODE_AP] : NULL,
.eht.cap = cmode ? &cmode->eht_capab[IEEE80211_MODE_AP] : NULL,
.eht.punct_bitmap = hostapd_get_punct_bitmap(hapd),
+ .s1g.enabled = iconf->ieee80211ah,
+ .s1g.op_class = iconf->op_class,
+ .s1g.primary_2mhz = hapd->iconf->s1g_primary_2mhz,
#ifdef CONFIG_IEEE80211BN
.uhr.enabled = iconf->ieee80211bn,
.uhr.dbe_bandwidth = iconf->dbe_bandwidth,
@@ -2930,6 +2994,8 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
for (i = 0; i < hapd->iface->num_hw_features; i++) {
mode = &hapd->iface->hw_features[i];
+ if (mode->mode == HOSTAPD_MODE_IEEE80211AH)
+ continue;
if (iconf->hw_mode != HOSTAPD_MODE_IEEE80211ANY &&
iconf->hw_mode != mode->mode)
continue;
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 57ec4f6ec12d..740282ce8bd0 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -224,6 +224,8 @@ static const char * hw_mode_str(enum hostapd_hw_mode mode)
return "a";
case HOSTAPD_MODE_IEEE80211AD:
return "ad";
+ case HOSTAPD_MODE_IEEE80211AH:
+ return "ah";
case HOSTAPD_MODE_IEEE80211ANY:
return "any";
case NUM_HOSTAPD_MODES:
@@ -900,6 +902,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
"ieee80211ax=%d\n"
"ieee80211be=%d\n"
"ieee80211bn=%d\n"
+ "ieee80211ah=%d\n"
"beacon_int=%u\n"
"dtim_period=%d\n",
iface->conf->channel,
@@ -912,6 +915,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
hostapd_is_he_enabled(hapd),
hostapd_is_eht_enabled(hapd),
hostapd_is_uhr_enabled(hapd),
+ iface->conf->ieee80211ah,
iface->conf->beacon_int,
hapd->conf->dtim_period);
if (os_snprintf_error(buflen - len, ret))
@@ -1189,7 +1193,9 @@ static struct hostapd_hw_modes * get_target_hw_mode(struct hostapd_iface *iface,
enum hostapd_hw_mode target_mode;
bool is_6ghz = is_6ghz_freq(freq);
- if (freq < 4000)
+ if (freq > 0 && freq < 1000)
+ target_mode = HOSTAPD_MODE_IEEE80211AH;
+ else if (freq < 4000)
target_mode = HOSTAPD_MODE_IEEE80211G;
else if (freq > 50000)
target_mode = HOSTAPD_MODE_IEEE80211AD;
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index caba71537b5b..909af3c281f9 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -839,7 +839,7 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
int skip_radar = 0;
- if (is_6ghz_freq(iface->freq))
+ if (is_6ghz_freq(iface->freq) || is_s1ghz_freq(iface->freq))
return 1;
if (!iface->current_mode) {
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 0503322d5d88..b493c40bd7bf 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -134,7 +134,8 @@ static void hostapd_reload_bss(struct hostapd_data *hapd)
if (hapd->conf->wmm_enabled < 0)
hapd->conf->wmm_enabled = hapd->iconf->ieee80211n |
- hapd->iconf->ieee80211ax;
+ hapd->iconf->ieee80211ax |
+ hapd->iconf->ieee80211ah;
#ifndef CONFIG_NO_RADIUS
radius_client_reconfig(hapd->radius, hapd->conf->radius);
@@ -1586,7 +1587,8 @@ setup_mld:
if (conf->wmm_enabled < 0)
conf->wmm_enabled = hapd->iconf->ieee80211n |
- hapd->iconf->ieee80211ax;
+ hapd->iconf->ieee80211ax |
+ hapd->iconf->ieee80211ah;
#ifdef CONFIG_IEEE80211R_AP
if (is_zero_ether_addr(conf->r1_key_holder))
@@ -2714,6 +2716,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
hapd->iconf->ieee80211ac,
hapd->iconf->ieee80211ax,
hapd->iconf->ieee80211be,
+ hapd->iconf->ieee80211ah,
hapd->iconf->secondary_channel,
hostapd_get_oper_chwidth(hapd->iconf),
hostapd_get_oper_centr_freq_seg0_idx(
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index d7715206ce9e..d64fc545783b 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -263,6 +263,12 @@ int hostapd_prepare_rates(struct hostapd_data *hapd,
int basic_rates_g[] = { 10, 20, 55, 110, 0 };
const int *basic_rates;
+#ifdef CONFIG_IEEE80211AH
+ /* S1G does not support basic rates */
+ if (mode->mode == HOSTAPD_MODE_IEEE80211AH)
+ return 0;
+#endif /* CONFIG_IEEE80211AH */
+
if (conf->basic_rates)
basic_rates = conf->basic_rates;
else switch (mode->mode) {
@@ -1207,6 +1213,11 @@ int hostapd_is_usable_chans(struct hostapd_iface *iface)
return err;
}
+#ifdef CONFIG_IEEE80211AH
+ if (iface->conf->ieee80211ah)
+ return 1;
+#endif /* CONFIG_IEEE80211AH */
+
if (!iface->conf->secondary_channel)
return 1;
@@ -1269,7 +1280,9 @@ int hostapd_determine_mode(struct hostapd_iface *iface)
iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)
return 0;
- if (iface->freq < 4000)
+ if (iface->freq > 0 && iface->freq < 1000)
+ target_mode = HOSTAPD_MODE_IEEE80211AH;
+ else if (iface->freq < 4000)
target_mode = HOSTAPD_MODE_IEEE80211G;
else if (iface->freq > 50000)
target_mode = HOSTAPD_MODE_IEEE80211AD;
@@ -1445,6 +1458,18 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
iface->conf->ieee80211be = 0;
}
+ if (iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211AH &&
+ (iface->conf->ieee80211n || iface->conf->ieee80211ac ||
+ iface->conf->ieee80211ax || iface->conf->ieee80211be ||
+ iface->conf->ieee80211bn)) {
+ wpa_printf(MSG_INFO, "Disable HT/VHT/HE/EHT/UHR for S1G mode");
+ iface->conf->ieee80211n = 0;
+ iface->conf->ieee80211ac = 0;
+ iface->conf->ieee80211ax = 0;
+ iface->conf->ieee80211be = 0;
+ iface->conf->ieee80211bn = 0;
+ }
+
iface->current_mode = NULL;
for (i = 0; i < iface->num_hw_features; i++) {
struct hostapd_hw_modes *mode = &iface->hw_features[i];
@@ -1507,6 +1532,8 @@ const char * hostapd_hw_mode_txt(int mode)
return "IEEE 802.11g";
case HOSTAPD_MODE_IEEE80211AD:
return "IEEE 802.11ad";
+ case HOSTAPD_MODE_IEEE80211AH:
+ return "IEEE 802.11ah";
default:
return "UNKNOWN";
}
@@ -1554,6 +1581,8 @@ int hostapd_hw_skip_mode(struct hostapd_iface *iface,
{
int i;
+ if (mode->mode == HOSTAPD_MODE_IEEE80211AH && !iface->current_mode)
+ return 1;
if (iface->current_mode)
return mode != iface->current_mode;
if (mode->mode != HOSTAPD_MODE_IEEE80211B)
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 219a94b0f9f7..ffa232f11d6b 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -522,6 +522,12 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
u8 buf[100];
size_t len;
+#ifdef CONFIG_IEEE80211AH
+ /* No supp rates for S1G */
+ if (hapd->iconf->ieee80211ah)
+ return eid;
+#endif /* CONFIG_IEEE80211AH */
+
len = hostapd_supp_rates(hapd, buf);
if (len == 0)
return eid;
@@ -5522,9 +5528,10 @@ static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
struct ieee802_11_elems *elems)
{
- /* Supported rates not used in IEEE 802.11ad/DMG */
- if (hapd->iface->current_mode &&
- hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD)
+ /* Supported rates not used in IEEE 802.11ad/DMG/ah */
+ if ((hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) ||
+ hapd->iconf->ieee80211ah)
return WLAN_STATUS_SUCCESS;
if (!elems->supp_rates) {
@@ -6113,6 +6120,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_IEEE80211BN */
+#ifdef CONFIG_IEEE80211AH
+ if (hapd->iconf->ieee80211ah) {
+ resp = copy_sta_s1g_capab(hapd, sta, IEEE80211_MODE_AP,
+ elems->s1g_capabilities);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+#endif /* CONFIG_IEEE80211AH */
+
#ifdef CONFIG_P2P
if (elems->p2p && ies && ies_len) {
wpabuf_free(sta->p2p_ie);
@@ -7274,7 +7290,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
sta->flags & WLAN_STA_EHT ? sta->eht_capab_len : 0,
sta->flags & WLAN_STA_UHR ? &uhr_cap : NULL,
sta->flags & WLAN_STA_UHR ? sta->uhr_capab_len : 0,
- sta->he_6ghz_capab,
+ sta->he_6ghz_capab, sta->s1g_capab,
sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
sta->vht_opmode, sta->p2p_ie ? 1 : 0,
set, mld_link_addr, mld_link_sta, eml_cap,
@@ -7309,6 +7325,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
struct ieee80211_mgmt *reply;
u8 *p;
u16 res = WLAN_STATUS_SUCCESS;
+ u16 aid;
buflen = sizeof(struct ieee80211_mgmt) + 1024;
#ifdef CONFIG_FILS
@@ -7365,15 +7382,23 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
send_len = IEEE80211_HDRLEN;
- send_len += sizeof(reply->u.assoc_resp);
reply->u.assoc_resp.capab_info =
host_to_le16(hostapd_own_capab_info(hapd));
reply->u.assoc_resp.status_code = host_to_le16(status_code);
- reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) |
- BIT(14) | BIT(15));
+ aid = sta ? sta->aid : 0;
+ if (hapd->iconf->ieee80211ah) {
+ /* S1G AID is carried in the AID Response element */
+ send_len += sizeof(reply->u.s1g_assoc_resp);
+ p = reply->u.s1g_assoc_resp.variable;
+ } else {
+ send_len += sizeof(reply->u.assoc_resp);
+ reply->u.assoc_resp.aid = host_to_le16(aid | BIT(14) | BIT(15));
+ p = reply->u.assoc_resp.variable;
+ }
+
/* Supported rates */
- p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
+ p = hostapd_eid_supp_rates(hapd, p);
/* Extended supported rates */
p = hostapd_eid_ext_supp_rates(hapd, p);
@@ -7464,6 +7489,14 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211AH
+ if (hapd->iconf->ieee80211ah) {
+ p = hostapd_eid_s1g_capab(hapd, p);
+ p = hostapd_eid_s1g_oper(hapd, p);
+ p = hostapd_eid_aid_response(hapd, p, aid);
+ }
+#endif /* CONFIG_IEEE80211AH */
+
p = hostapd_eid_ext_capab(hapd, p, false);
p = hostapd_eid_bss_max_idle_period(hapd, p,
sta ? sta->max_idle_period : 0);
@@ -7714,7 +7747,11 @@ rsnxe_done:
p += wpabuf_len(hapd->conf->assocresp_elements);
}
- send_len += p - reply->u.assoc_resp.variable;
+ if (hapd->iconf->ieee80211ah) {
+ send_len += p - reply->u.s1g_assoc_resp.variable;
+ } else {
+ send_len += p - reply->u.assoc_resp.variable;
+ }
#ifdef CONFIG_FILS
if (sta &&
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index da87f0b29959..01e101313f15 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -103,6 +103,10 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_s1g_oper(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_s1g_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_s1g_beacon_compat(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_aid_response(struct hostapd_data *hapd, u8 *eid, u16 aid);
int hostapd_ht_operation_update(struct hostapd_iface *iface);
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -192,6 +196,8 @@ int hostapd_update_time_adv(struct hostapd_data *hapd);
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid,
u16 value);
+u16 copy_sta_s1g_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ enum ieee80211_op_mode opmode, const u8 *s1g_capab);
int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
#ifdef CONFIG_SAE
diff --git a/src/ap/ieee802_11_s1g.c b/src/ap/ieee802_11_s1g.c
new file mode 100644
index 000000000000..75b55ba22054
--- /dev/null
+++ b/src/ap/ieee802_11_s1g.c
@@ -0,0 +1,202 @@
+/*
+ * hostapd / IEEE 802.11ah S1G
+ * Copyright (c) 2022, Morse Micro
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+
+/* 1SS, MCS9. TODO set via config */
+#define S1G_BASIC_MCS_NSS 0xCCC4
+
+static u8 hostapd_s1g_calc_pri_1mhz_loc(struct hostapd_data *hapd)
+{
+ u8 low_chan;
+ u8 pri_1mhz_chan = hapd->iconf->channel;
+ u8 oper_ch = hapd->iconf->s1g_oper_centr_freq_idx ?
+ hapd->iconf->s1g_oper_centr_freq_idx :
+ hapd->iconf->channel;
+
+ switch (hapd->iconf->s1g_oper_chwidth) {
+ case CONF_OPER_CHWIDTH_2MHZ:
+ low_chan = oper_ch - 1;
+ break;
+ case CONF_OPER_CHWIDTH_4MHZ:
+ low_chan = oper_ch - 3;
+ break;
+ case CONF_OPER_CHWIDTH_8MHZ:
+ low_chan = oper_ch - 7;
+ break;
+ case CONF_OPER_CHWIDTH_16MHZ:
+ low_chan = oper_ch - 15;
+ break;
+ default:
+ return 0;
+ }
+
+ return ((pri_1mhz_chan - low_chan) / 2) & 1;
+}
+
+
+u8 * hostapd_eid_s1g_oper(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_s1g_operation *cap;
+ u8 *pos = eid;
+ u8 ch_width = 0;
+ u8 bss_width;
+ u8 s1g_pri_loc = hostapd_s1g_calc_pri_1mhz_loc(hapd);
+
+ /* IEEE80211-2024 Table 10-39 */
+ switch (hapd->iconf->s1g_oper_chwidth) {
+ case CONF_OPER_CHWIDTH_1MHZ:
+ bss_width = 0x0;
+ break;
+ case CONF_OPER_CHWIDTH_2MHZ:
+ bss_width = 0x1;
+ break;
+ case CONF_OPER_CHWIDTH_4MHZ:
+ bss_width = 0x3;
+ break;
+ case CONF_OPER_CHWIDTH_8MHZ:
+ bss_width = 0x7;
+ break;
+ case CONF_OPER_CHWIDTH_16MHZ:
+ bss_width = 0xF;
+ break;
+ default:
+ return eid;
+ }
+
+ *pos++ = WLAN_EID_S1G_OPERATION;
+ *pos++ = sizeof(*cap);
+
+ cap = (struct ieee80211_s1g_operation *) pos;
+ os_memset(cap, 0, sizeof(*cap));
+
+ if (!hapd->iconf->s1g_primary_2mhz)
+ ch_width |= S1G_OPER_IE_CHANWIDTH_PRIM_CH_MASK;
+
+ ch_width |= (bss_width << 1) & S1G_OPER_IE_CHANWIDTH_OPER_CH_MASK;
+
+ if (hapd->iconf->s1g_primary_2mhz && s1g_pri_loc)
+ ch_width |= S1G_OPER_IE_CHANWIDTH_PRIM_OFFSET;
+
+ cap->ch_width = ch_width;
+ cap->oper_class =
+ global_op_class_to_s1g_op_class(hapd->iconf->op_class);
+ cap->basic_mcs_nss = host_to_le16(S1G_BASIC_MCS_NSS);
+
+ if (hapd->iconf->s1g_primary_2mhz) {
+ if (s1g_pri_loc)
+ cap->primary_ch = hapd->iconf->channel - 1;
+ else
+ cap->primary_ch = hapd->iconf->channel + 1;
+ } else {
+ cap->primary_ch = hapd->iconf->channel;
+ }
+
+ cap->oper_ch = hapd->iconf->s1g_oper_centr_freq_idx ?
+ hapd->iconf->s1g_oper_centr_freq_idx :
+ cap->primary_ch;
+
+ pos += sizeof(*cap);
+ return pos;
+}
+
+
+u8 * hostapd_eid_s1g_capab(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_s1g_capabilities *cap;
+ u8 *pos = eid;
+
+ *pos++ = WLAN_EID_S1G_CAPABILITIES;
+ *pos++ = sizeof(*cap);
+
+ cap = (struct ieee80211_s1g_capabilities *) pos;
+ os_memcpy(cap, &hapd->iface->current_mode->s1g_capab, sizeof(*cap));
+
+ pos += sizeof(*cap);
+ return pos;
+}
+
+
+u8 * hostapd_eid_s1g_beacon_compat(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u16 beacon_int;
+
+ beacon_int = hapd->iconf->beacon_int;
+
+ *pos++ = WLAN_EID_S1G_BCN_COMPAT;
+ *pos++ = 8;
+ WPA_PUT_LE16(pos, hostapd_own_capab_info(hapd));
+ pos += 2;
+
+ WPA_PUT_LE16(pos, beacon_int);
+ pos += 2;
+
+ /*
+ * Last 4 octets will be filled by the driver/fw to contain the 4
+ * MSB of the TSF timer at time of generation.
+ */
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ return pos;
+}
+
+
+u8 * hostapd_eid_aid_response(struct hostapd_data *hapd, u8 *eid, u16 aid)
+{
+ u8 *pos = eid;
+
+ *pos++ = WLAN_EID_AID_RESPONSE;
+ *pos++ = 5;
+
+ /* AID/MID */
+ WPA_PUT_LE16(pos, aid);
+ pos += 2;
+ /* AID Switch Count */
+ *pos++ = 0;
+ /* AID Response Interval */
+ WPA_PUT_LE16(pos, 0);
+ pos += 2;
+
+ return pos;
+}
+
+
+u16 copy_sta_s1g_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ enum ieee80211_op_mode opmode, const u8 *s1g_capab)
+{
+ if (!s1g_capab) {
+ os_free(sta->s1g_capab);
+ sta->s1g_capab = NULL;
+ sta->flags &= ~WLAN_STA_S1G;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (!sta->s1g_capab) {
+ sta->s1g_capab =
+ os_zalloc(sizeof(struct ieee80211_s1g_capabilities));
+ if (!sta->s1g_capab)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ os_memcpy(sta->s1g_capab, s1g_capab,
+ sizeof(struct ieee80211_s1g_capabilities));
+ sta->flags |= WLAN_STA_S1G;
+
+ return WLAN_STATUS_SUCCESS;
+}
diff --git a/src/ap/interference.c b/src/ap/interference.c
index 44aa6c0019ef..12ab133c32d6 100644
--- a/src/ap/interference.c
+++ b/src/ap/interference.c
@@ -558,6 +558,7 @@ int hostapd_incumbt_sig_intf_detected(struct hostapd_iface *iface, int freq,
iface->conf->ieee80211ac,
iface->conf->ieee80211ax,
iface->conf->ieee80211be,
+ iface->conf->ieee80211ah,
iface->conf->secondary_channel,
hostapd_get_oper_chwidth(iface->conf),
hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 4326f1344277..cf34c7644dfe 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -515,6 +515,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
os_free(sta->he_6ghz_capab);
os_free(sta->eht_capab);
os_free(sta->uhr_capab);
+ os_free(sta->s1g_capab);
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);
@@ -2155,7 +2156,7 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
if (hostapd_sta_add(hapd, sta->addr, 0, 0,
sta->supported_rates,
sta->supported_rates_len,
- 0, NULL, NULL, NULL, 0, NULL, 0, NULL, 0, NULL,
+ 0, NULL, NULL, NULL, 0, NULL, 0, NULL, 0, NULL, NULL,
sta->flags, 0, 0, 0, 0,
mld_link_addr, mld_link_sta, eml_cap, epp_sta)) {
hostapd_logger(hapd, sta->addr,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 6dd3ce4ae929..39ac0b43d848 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -50,6 +50,7 @@
#define WLAN_STA_PENDING_DEAUTH_CB BIT_ULL(30)
#define WLAN_STA_NONERP BIT_ULL(31)
#define WLAN_STA_UHR BIT_ULL(32)
+#define WLAN_STA_S1G BIT_ULL(33)
/* Maximum number of supported rates (from both Supported Rates and Extended
* Supported Rates IEs). */
@@ -228,6 +229,7 @@ struct sta_info {
size_t eht_capab_len;
struct ieee80211_uhr_capabilities *uhr_capab;
size_t uhr_capab_len;
+ struct ieee80211_s1g_capabilities *s1g_capab;
int sa_query_count; /* number of pending SA Query requests;
* 0 = no SA Query in progress */
diff --git a/src/common/defs.h b/src/common/defs.h
index d6bd21b38baf..86e6893ed9b7 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -423,6 +423,7 @@ enum hostapd_hw_mode {
HOSTAPD_MODE_IEEE80211A,
HOSTAPD_MODE_IEEE80211AD,
HOSTAPD_MODE_IEEE80211ANY,
+ HOSTAPD_MODE_IEEE80211AH,
NUM_HOSTAPD_MODES
};
@@ -514,6 +515,11 @@ enum oper_chan_width {
CONF_OPER_CHWIDTH_8640MHZ,
CONF_OPER_CHWIDTH_40MHZ_6GHZ,
CONF_OPER_CHWIDTH_320MHZ,
+ CONF_OPER_CHWIDTH_1MHZ,
+ CONF_OPER_CHWIDTH_2MHZ,
+ CONF_OPER_CHWIDTH_4MHZ,
+ CONF_OPER_CHWIDTH_8MHZ,
+ CONF_OPER_CHWIDTH_16MHZ,
};
enum key_flag {
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index 0d4f47cf796d..24607fa4a71d 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -575,6 +575,8 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
const struct eht_capabilities *eht_cap = info->eht.cap;
u16 punct_bitmap = info->eht.punct_bitmap;
bool uhr_enabled = info->uhr.enabled;
+ int s1g_enabled = info->s1g.enabled;
+ bool s1g_primary_2mhz = info->s1g.primary_2mhz;
enum oper_chan_width oper_chwidth_legacy;
u8 seg0_legacy, seg1_legacy;
@@ -594,10 +596,12 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
data->vht_enabled = vht_enabled;
data->he_enabled = he_enabled;
data->eht_enabled = eht_enabled;
+ data->s1g_enabled = s1g_enabled;
data->sec_channel_offset = sec_channel_offset;
data->center_freq1 = freq + sec_channel_offset * 10;
data->center_freq2 = 0;
data->punct_bitmap = punct_bitmap;
+ data->s1g_primary_2mhz = s1g_primary_2mhz;
if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
data->bandwidth = 80;
else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
@@ -614,7 +618,41 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
hostapd_encode_edmg_chan(enable_edmg, edmg_channel, channel,
&data->edmg);
- if (is_6ghz_freq(freq)) {
+ if (is_s1ghz_freq(freq)) {
+ if (oper_chwidth == CONF_OPER_CHWIDTH_16MHZ)
+ data->bandwidth = 16;
+ else if (oper_chwidth == CONF_OPER_CHWIDTH_8MHZ)
+ data->bandwidth = 8;
+ else if (oper_chwidth == CONF_OPER_CHWIDTH_4MHZ)
+ data->bandwidth = 4;
+ else if (oper_chwidth == CONF_OPER_CHWIDTH_2MHZ)
+ data->bandwidth = 2;
+ else
+ data->bandwidth = 1;
+
+ if (!center_segment0) {
+ data->center_freq1 = data->freq;
+ data->center_freq1_offset = data->freq_offset;
+ } else {
+ int freq1 = ieee80211_chan_to_freq_khz(
+ NULL, info->s1g.op_class, center_segment0);
+ if (freq1 < 0) {
+ wpa_printf(MSG_ERROR,
+ "Invalid segment 0 center frequency for S1G");
+ return -1;
+ }
+
+ data->center_freq1 = KHZ_TO_MHZ(freq1);
+ data->center_freq1_offset = KHZ_TO_S1G_OFFSET(freq1);
+ }
+
+ data->ht_enabled = 0;
+ data->vht_enabled = 0;
+ data->he_enabled = 0;
+ data->eht_enabled = 0;
+ data->center_freq2 = 0;
+ return 0;
+ } else if (is_6ghz_freq(freq)) {
if (!data->he_enabled && !data->eht_enabled) {
wpa_printf(MSG_ERROR,
"Can't set 6 GHz mode - HE or EHT aren't enabled");
@@ -1060,6 +1098,12 @@ int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
u32 bw_mask;
switch (bw) {
+ case 4:
+ return chan->allowed_bw & HOSTAPD_CHAN_WIDTH_4;
+ case 8:
+ return chan->allowed_bw & HOSTAPD_CHAN_WIDTH_8;
+ case 16:
+ return chan->allowed_bw & HOSTAPD_CHAN_WIDTH_16;
case 20:
bw_mask = HOSTAPD_CHAN_WIDTH_20;
break;
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index 47333a7bd7de..088b9ebb4a9d 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -87,6 +87,13 @@ struct hostapd_channel_info {
u8 dbe_bandwidth;
u16 dbe_punct_bitmap;
} uhr;
+
+ struct {
+ int enabled;
+ /* Global operating class */
+ u8 op_class;
+ bool primary_2mhz;
+ } s1g;
};
int hostapd_set_freq_params(struct hostapd_freq_params *data,
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index eaf6310911a9..fc2fc09f505f 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -761,7 +761,7 @@ static ParseRes __ieee802_11_parse_elems(const u8 *start, size_t len,
case WLAN_EID_S1G_CAPABILITIES:
if (elen < 15)
break;
- elems->s1g_capab = pos;
+ elems->s1g_capabilities = pos;
break;
case WLAN_EID_FRAGMENT:
wpa_printf(MSG_MSGDUMP,
@@ -773,6 +773,16 @@ static ParseRes __ieee802_11_parse_elems(const u8 *start, size_t len,
len, show_errors))
unknown++;
break;
+ case WLAN_EID_S1G_OPERATION:
+ if (elen < sizeof(struct ieee80211_s1g_operation))
+ break;
+ elems->s1g_operation = pos;
+ break;
+ case WLAN_EID_AID_RESPONSE:
+ if (elen < sizeof(struct ieee80211_aid_response))
+ break;
+ elems->aid_response = pos;
+ break;
default:
unknown++;
if (!show_errors)
@@ -976,7 +986,10 @@ void ieee802_11_elems_clear_ids(struct ieee802_11_elems *elems,
elems->dils_len = 0;
break;
case WLAN_EID_S1G_CAPABILITIES:
- elems->s1g_capab = NULL;
+ elems->s1g_capabilities = NULL;
+ break;
+ case WLAN_EID_S1G_OPERATION:
+ elems->s1g_operation = NULL;
break;
}
}
@@ -1586,6 +1599,36 @@ ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel,
/* TODO: more operating classes */
+ if (is_s1ghz_freq(freq)) {
+ if (freq < 902 || freq > 927)
+ return NUM_HOSTAPD_MODES;
+
+ *channel = (MHZ_TO_KHZ(freq) + 500 - MHZ_TO_KHZ(902)) / 500;
+
+ switch (chanwidth) {
+ case CONF_OPER_CHWIDTH_1MHZ:
+ *op_class = 68;
+ break;
+ case CONF_OPER_CHWIDTH_2MHZ:
+ *op_class = 69;
+ break;
+ case CONF_OPER_CHWIDTH_4MHZ:
+ *op_class = 70;
+ break;
+ case CONF_OPER_CHWIDTH_8MHZ:
+ *op_class = 71;
+ break;
+ case CONF_OPER_CHWIDTH_16MHZ:
+ *op_class = 72;
+ break;
+ default:
+ *op_class = 0;
+ break;
+ }
+
+ return HOSTAPD_MODE_IEEE80211AH;
+ }
+
if (sec_channel > 1 || sec_channel < -1)
return NUM_HOSTAPD_MODES;
@@ -2093,6 +2136,14 @@ static int ieee80211_chan_to_freq_khz_global(u8 op_class, u8 chan)
{
/* Table E-4 in IEEE Std 802.11-2020 - Global operating classes */
switch (op_class) {
+ case 68:
+ case 69:
+ case 70:
+ case 71:
+ case 72:
+ if (chan < 1 || chan > 51)
+ return -1;
+ return 902000 + chan * 500;
case 81:
/* channels 1..13 */
if (chan < 1 || chan > 13)
@@ -3224,6 +3275,12 @@ bool is_same_band(int freq1, int freq2)
}
+bool is_s1ghz_freq(int freq)
+{
+ return freq >= 750 && freq < 1000;
+}
+
+
int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
size_t nei_rep_len)
{
@@ -3512,6 +3569,16 @@ int op_class_to_bandwidth(u8 op_class)
enum oper_chan_width op_class_to_ch_width(u8 op_class)
{
switch (op_class) {
+ case 68: /* S1G 2024 US 1MHz */
+ return CONF_OPER_CHWIDTH_1MHZ;
+ case 69: /* S1G 2024 US 2MHz */
+ return CONF_OPER_CHWIDTH_2MHZ;
+ case 70: /* S1G 2024 US 4MHz */
+ return CONF_OPER_CHWIDTH_4MHZ;
+ case 71: /* S1G 2024 US 8MHz */
+ return CONF_OPER_CHWIDTH_8MHZ;
+ case 72: /* S1G 2024 US 16MHz */
+ return CONF_OPER_CHWIDTH_16MHZ;
case 81:
case 82:
return CONF_OPER_CHWIDTH_USE_HT;
@@ -4407,3 +4474,31 @@ int ieee80211_get_center_freq(int ctrl_freq, u32 bw)
return -1;
}
}
+
+
+/**
+ * global_op_class_to_s1g_op_class - Convert a global operating class to S1G
+ * @op_class: Global operating class (IEEE Std 802.11-2024, Table E-4)
+ * Returns: The matching S1G operating class (IEEE Std 802.11-2024, Table E-5)
+ * or 0 if the operating class is not a supported S1G operating class
+ *
+ * Only the S1G operating classes for the United States are currently
+ * supported.
+ */
+u8 global_op_class_to_s1g_op_class(u8 op_class)
+{
+ switch (op_class) {
+ case 68:
+ return 1;
+ case 69:
+ return 2;
+ case 70:
+ return 3;
+ case 71:
+ return 4;
+ case 72:
+ return 5;
+ default:
+ return 0;
+ }
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index ab0031cebe47..bd07530c8c1a 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -146,7 +146,7 @@ struct ieee802_11_elems {
const u8 *short_ssid_list;
const u8 *he_6ghz_band_cap;
const u8 *sae_pk;
- const u8 *s1g_capab;
+ const u8 *s1g_capabilities;
const u8 *pasn_params;
const u8 *eht_capabilities;
const u8 *eht_operation;
@@ -170,6 +170,8 @@ struct ieee802_11_elems {
const u8 *uhr_operation;
const u8 *security_profile;
const u8 *pqc_parameter;
+ const u8 *s1g_operation;
+ const u8 *aid_response;
u8 ssid_len;
u8 supp_rates_len;
@@ -374,6 +376,8 @@ int get_6ghz_sec_channel(int channel);
bool is_same_band(int freq1, int freq2);
#define IS_2P4GHZ(n) (n >= 2412 && n <= 2484)
#define IS_5GHZ(n) (n > 4000 && n < 5895)
+bool is_s1ghz_freq(int freq);
+u8 global_op_class_to_s1g_op_class(u8 op_class);
u8 op_class_idx_to_chan(const struct oper_class_map *op, u8 idx);
int op_class_chan_to_idx(const struct oper_class_map *op, u8 chan);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 20edcae0b8d9..1b0cba393a24 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -89,6 +89,21 @@
#define WLAN_FC_STYPE_DMG_BEACON 0
#define WLAN_FC_STYPE_S1G_BEACON 1
+/* IEEE Std 802.11-2024, Table 9-8 (Frame Control field BSS BW setting) */
+#define WLAN_S1G_BSS_BW_MIN_1_MAX_2 0
+#define WLAN_S1G_BSS_BW_EQUAL_TO_PPDU 1
+#define WLAN_S1G_BSS_BW_MIN_1_MAX_4 2
+#define WLAN_S1G_BSS_BW_MIN_2_MAX_4 3
+#define WLAN_S1G_BSS_BW_MIN_1_MAX_8 4
+#define WLAN_S1G_BSS_BW_MIN_2_MAX_8 5
+#define WLAN_S1G_BSS_BW_MIN_1_MAX_16 6
+#define WLAN_S1G_BSS_BW_MIN_2_MAX_16 7
+
+#define WLAN_S1G_FC_BSS_BW_SHIFT 11
+#define WLAN_S1G_FC_BSS_BW_MASK (0x7 << WLAN_S1G_FC_BSS_BW_SHIFT)
+#define WLAN_S1G_FC_BSS_BW(bw) (((bw) & 0x7) << \
+ WLAN_S1G_FC_BSS_BW_SHIFT)
+
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
@@ -490,7 +505,10 @@
#define WLAN_EID_DEVICE_LOCATION 204
#define WLAN_EID_WHITE_SPACE_MAP 205
#define WLAN_EID_FTM_PARAMETERS 206
+#define WLAN_EID_AID_REQUEST 210
+#define WLAN_EID_AID_RESPONSE 211
#define WLAN_EID_S1G_BCN_COMPAT 213
+#define WLAN_EID_S1G_SHORT_BCN_INTERVAL 214
#define WLAN_EID_TWT 216
#define WLAN_EID_S1G_CAPABILITIES 217
#define WLAN_EID_VENDOR_SPECIFIC 221
@@ -1085,23 +1103,6 @@ struct ieee80211_hdr {
#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr))
-struct ieee80211_hdr_s1g_beacon {
- le16 frame_control;
- le16 duration_id;
- u8 sa[6];
- u8 timestamp[4];
- u8 change_seq[1];
- /* followed by:
- * 'u8 next_tbtt[3];' if the Next TBTT Present field in the
- * Frame Control field is 1
- * 'u8 compressed_ssid[4];' if the Compressed SSID Present field in
- * the Frame Control is 1
- * 'u8 ano[1];' if the ANO field in the Frame Control field is 1
- */
-} STRUCT_PACKED;
-
-#define IEEE80211_HDRLEN_S1G_BEACON (sizeof(struct ieee80211_hdr_s1g_beacon))
-
#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
struct ieee80211_mgmt {
@@ -1136,6 +1137,11 @@ struct ieee80211_mgmt {
/* followed by Supported rates */
u8 variable[];
} STRUCT_PACKED assoc_resp, reassoc_resp;
+ struct {
+ le16 capab_info;
+ le16 status_code;
+ u8 variable[];
+ } STRUCT_PACKED s1g_assoc_resp, s1g_reassoc_resp;
struct {
le16 capab_info;
le16 listen_interval;
@@ -1307,6 +1313,29 @@ struct ieee80211_mgmt {
} u;
} STRUCT_PACKED;
+struct ieee80211_ext {
+ le16 frame_control;
+ le16 duration;
+ u8 sa[6];
+ union {
+ struct {
+ u8 timestamp[4];
+ u8 change_seq;
+ /* followed by:
+ * 'u8 next_tbtt[3];' if the Next TBTT Present field
+ * in the Frame Control field is 1
+ * 'u8 compressed_ssid[4];' if the Compressed SSID
+ * Present field in the Frame
+ * Control is 1
+ * 'u8 ano[1];' if the ANO field in the Frame Control
+ * field is 1
+ */
+ u8 variable[];
+ } STRUCT_PACKED beacon;
+ } u;
+} STRUCT_PACKED;
+
+#define IEEE80211_HDRLEN_S1G_BEACON (sizeof(struct ieee80211_ext))
#define IEEE80211_MIN_ACTION_LEN(type) \
(offsetof(struct ieee80211_mgmt, u.action.u.type) + \
@@ -2768,6 +2797,34 @@ struct ieee80211_he_mu_edca_parameter_set {
/* B7: Reserved if sent by an AP; More Data Ack if sent by a non-AP STA */
#define HE_QOS_INFO_MORE_DATA_ACK ((u8) (BIT(7)))
+struct ieee80211_s1g_capabilities {
+ u8 capab_info[10];
+ u8 supp_mcs_nss[5];
+} STRUCT_PACKED;
+
+struct ieee80211_s1g_operation {
+ u8 ch_width;
+ u8 oper_class;
+ u8 primary_ch;
+ u8 oper_ch;
+ le16 basic_mcs_nss;
+} STRUCT_PACKED;
+
+struct ieee80211_aid_response {
+ le16 aid;
+ u8 switch_count;
+ le16 response_interval;
+} STRUCT_PACKED;
+
+#define S1G_OPER_IE_CHANWIDTH_PRIM_CH_MASK (BIT(0))
+#define S1G_OPER_IE_CHANWIDTH_OPER_CH_MASK (BIT(1) | BIT(2) | \
+ BIT(3) | BIT(4))
+#define S1G_OPER_IE_CHANWIDTH_PRIM_OFFSET (BIT(5))
+#define S1G_OPER_IE_MAX_MCS_1_NSS_SHIFT (2)
+#define S1G_OPER_IE_MAX_MCS_2_NSS_SHIFT (6)
+#define S1G_OPER_IE_MAX_MCS_3_NSS_SHIFT (10)
+#define S1G_OPER_IE_MAX_MCS_4_NSS_SHIFT (14)
+
/*
* IEEE Std 802.11-2020 and IEEE Std 802.11ax-2021
* 9.4.2.170 Reduced Neighbor Report element
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index f8c854227011..7131923de327 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -63,6 +63,9 @@ enum hostapd_chan_width_attr {
HOSTAPD_CHAN_WIDTH_80 = BIT(4),
HOSTAPD_CHAN_WIDTH_160 = BIT(5),
HOSTAPD_CHAN_WIDTH_320 = BIT(6),
+ HOSTAPD_CHAN_WIDTH_4 = BIT(9),
+ HOSTAPD_CHAN_WIDTH_8 = BIT(10),
+ HOSTAPD_CHAN_WIDTH_16 = BIT(11),
};
/* Filter gratuitous ARP */
@@ -344,6 +347,11 @@ struct hostapd_hw_modes {
* uhr_capab - UHR (IEEE 802.11bn) capabilities
*/
struct uhr_capabilities uhr_capab[IEEE80211_MODE_NUM];
+
+ /**
+ * s1g_capab - S1G (IEEE 802.11ah) capabilities
+ */
+ struct ieee80211_s1g_capabilities s1g_capab;
};
@@ -900,6 +908,11 @@ struct hostapd_freq_params {
*/
int he_enabled;
+ /**
+ * s1g_enabled - Whether S1G is enabled
+ */
+ int s1g_enabled;
+
/**
* center_freq1 - Segment 0 center frequency in MHz
*
@@ -922,7 +935,7 @@ struct hostapd_freq_params {
int center_freq2;
/**
- * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160)
+ * bandwidth - Channel bandwidth in MHz (1, 2, 4, 8, 16, 20, 40, 80, 160)
*/
int bandwidth;
@@ -954,6 +967,13 @@ struct hostapd_freq_params {
* link_id: If >=0 indicates the link of the AP MLD to configure
*/
int link_id;
+
+ /**
+ * s1g_primary_2mhz: Whether the S1G channel uses a 2 MHz primary
+ *
+ * S1G primary channels can either be 1MHz or 2MHz.
+ */
+ bool s1g_primary_2mhz;
};
/**
@@ -2941,6 +2961,8 @@ struct hostapd_sta_add_params {
const struct ieee80211_uhr_capabilities *uhr_capab;
size_t uhr_capab_len;
+ const struct ieee80211_s1g_capabilities *s1g_capab;
+
#ifdef CONFIG_NAN
/*
* For a station added to a NAN Data Interface (NDI) indicate the
diff --git a/wlantest/bip.c b/wlantest/bip.c
index 8fbf43ed6fad..ddfbdca7aacb 100644
--- a/wlantest/bip.c
+++ b/wlantest/bip.c
@@ -77,7 +77,7 @@ u8 * bip_protect_s1g_beacon(const u8 *igtk, size_t igtk_len, const u8 *frame,
u8 *prot, *pos, *buf;
u8 mic[16];
u16 fc;
- struct ieee80211_hdr_s1g_beacon *hdr;
+ const struct ieee80211_ext *hdr;
struct ieee80211_s1g_beacon_compat *bc;
size_t plen, mic_len, element_len, buf_len;
size_t hdr_add_len = 0, bce_add_len = 0;
@@ -121,7 +121,7 @@ u8 * bip_protect_s1g_beacon(const u8 *igtk, size_t igtk_len, const u8 *frame,
/* BIP AAD: FC SA ChangeSeq NextTBTT(if present)
* CompressedSSID(if present) ANO(if present), BIPN(if using BCE) */
- hdr = (struct ieee80211_hdr_s1g_beacon *) frame;
+ hdr = (const struct ieee80211_ext *) frame;
fc = le_to_host16(hdr->frame_control);
if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_EXT ||
WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_S1G_BEACON) {
@@ -133,7 +133,7 @@ u8 * bip_protect_s1g_beacon(const u8 *igtk, size_t igtk_len, const u8 *frame,
WPA_PUT_LE16(buf, fc);
os_memcpy(buf + 2, hdr->sa, ETH_ALEN);
- os_memcpy(buf + 8, hdr->change_seq, 1);
+ buf[8] = hdr->u.beacon.change_seq;
if (fc & WLAN_FC_S1G_BEACON_NEXT_TBTT)
hdr_add_len += 3;
if (fc & WLAN_FC_S1G_BEACON_COMP_SSID)
@@ -257,7 +257,7 @@ u8 * bip_gmac_protect_s1g_beacon(const u8 *igtk, size_t igtk_len,
{
u8 *prot, *pos, *buf;
u16 fc;
- struct ieee80211_hdr_s1g_beacon *hdr;
+ const struct ieee80211_ext *hdr;
struct ieee80211_s1g_beacon_compat *bc;
size_t plen, mic_len, element_len, buf_len;
size_t hdr_add_len = 0, bce_add_len = 0;
@@ -301,7 +301,7 @@ u8 * bip_gmac_protect_s1g_beacon(const u8 *igtk, size_t igtk_len,
/* BIP AAD: FC SA ChangeSeq NextTBTT(if present)
* CompressedSSID(if present) ANO(if present) */
- hdr = (struct ieee80211_hdr_s1g_beacon *) frame;
+ hdr = (const struct ieee80211_ext *) frame;
fc = le_to_host16(hdr->frame_control);
if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_EXT ||
WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_S1G_BEACON) {
@@ -313,7 +313,7 @@ u8 * bip_gmac_protect_s1g_beacon(const u8 *igtk, size_t igtk_len,
WPA_PUT_LE16(buf, fc);
os_memcpy(buf + 2, hdr->sa, ETH_ALEN);
- os_memcpy(buf + 8, hdr->change_seq, 1);
+ buf[8] = hdr->u.beacon.change_seq;
if (fc & WLAN_FC_S1G_BEACON_NEXT_TBTT)
hdr_add_len += 3;
if (fc & WLAN_FC_S1G_BEACON_COMP_SSID)
diff --git a/wlantest/bss.c b/wlantest/bss.c
index aed678ba6127..34a5f8f64a17 100644
--- a/wlantest/bss.c
+++ b/wlantest/bss.c
@@ -178,7 +178,7 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
* Probe Response frames. Note this assumes short beacons were dropped
* due to missing SSID above.
*/
- if (!elems->rsn_ie && (!elems->s1g_capab || beacon != 1)) {
+ if (!elems->rsn_ie && (!elems->s1g_capabilities || beacon != 1)) {
if (bss->rsnie[0]) {
add_note(wt, MSG_INFO, "BSS " MACSTR
" - RSN IE removed", MAC2STR(bss->bssid));
--
2.43.0
More information about the Hostap
mailing list