[RFC PATCH] initial UHR support
Johannes Berg
johannes at sipsolutions.net
Thu Sep 25 15:51:31 PDT 2025
From: Johannes Berg <johannes.berg at intel.com>
Add initial UHR support, based on a very "superficial"
reading of D1.0 (it's incomplete and not well-specified
in quite a few places.)
Signed-off-by: Johannes Berg <johannes.berg at intel.com>
---
hostapd/Android.mk | 6 +
hostapd/Makefile | 6 +
hostapd/config_file.c | 8 ++
hostapd/hostapd.conf | 13 ++
src/ap/ap_config.h | 6 +
src/ap/ap_drv_ops.c | 4 +
src/ap/ap_drv_ops.h | 2 +
src/ap/beacon.c | 17 +++
src/ap/ctrl_iface_ap.c | 17 +++
src/ap/ieee802_11.c | 51 +++++++-
src/ap/ieee802_11.h | 13 ++
src/ap/ieee802_11_uhr.c | 146 ++++++++++++++++++++++
src/ap/sta_info.c | 10 +-
src/ap/sta_info.h | 65 +++++-----
src/common/ieee802_11_common.c | 16 +++
src/common/ieee802_11_common.h | 4 +
src/common/ieee802_11_defs.h | 23 ++++
src/drivers/driver.h | 22 ++++
src/drivers/driver_common.c | 7 ++
src/drivers/driver_nl80211.c | 14 +++
src/drivers/driver_nl80211_capa.c | 19 +++
src/drivers/driver_nl80211_event.c | 2 +
src/utils/common.h | 4 +
tests/hwsim/example-hostapd.config | 2 +
tests/hwsim/example-wpa_supplicant.config | 2 +
tests/hwsim/test_uhr.py | 145 +++++++++++++++++++++
tests/hwsim/vm/inside.sh | 2 +-
wpa_supplicant/Android.mk | 3 +
wpa_supplicant/Makefile | 3 +
wpa_supplicant/config.c | 1 +
wpa_supplicant/config_file.c | 1 +
wpa_supplicant/config_ssid.h | 8 ++
wpa_supplicant/ctrl_iface.c | 8 +-
wpa_supplicant/events.c | 14 +++
wpa_supplicant/sme.c | 1 +
wpa_supplicant/wpa_cli.c | 1 +
wpa_supplicant/wpa_supplicant.c | 17 ++-
wpa_supplicant/wpa_supplicant_i.h | 4 +
38 files changed, 645 insertions(+), 42 deletions(-)
create mode 100644 src/ap/ieee802_11_uhr.c
create mode 100644 tests/hwsim/test_uhr.py
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index d097971f2dd7..4f4d72539de7 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -299,6 +299,12 @@ ifdef CONFIG_IEEE80211AC
L_CFLAGS += -DCONFIG_IEEE80211AC
endif
+ifdef CONFIG_IEEE80211BN
+CONFIG_IEEE80211BE=y
+L_CFLAGS += -DCONFIG_IEEE80211BN
+OBJS += src/ap/ieee802_11_uhr.c
+endif
+
ifdef CONFIG_IEEE80211BE
CONFIG_IEEE80211AX=y
L_CFLAGS += -DCONFIG_IEEE80211BE
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 93a11dd86326..e966bd7d7d2c 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -344,6 +344,12 @@ ifdef CONFIG_IEEE80211AC
CFLAGS += -DCONFIG_IEEE80211AC
endif
+ifdef CONFIG_IEEE80211BN
+CONFIG_IEEE80211BE=y
+CFLAGS += -DCONFIG_IEEE80211BN
+OBJS += ../src/ap/ieee802_11_uhr.o
+endif
+
ifdef CONFIG_IEEE80211BE
CONFIG_IEEE80211AX=y
CFLAGS += -DCONFIG_IEEE80211BE
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 983dfad0a3f8..57f3355a9522 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -4859,6 +4859,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->disable_11ax = !!atoi(pos);
} else if (os_strcmp(buf, "disable_11be") == 0) {
bss->disable_11be = !!atoi(pos);
+ } else if (os_strcmp(buf, "disable_11bn") == 0) {
+ bss->disable_11bn = !!atoi(pos);
#ifdef CONFIG_PASN
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcmp(buf, "force_kdk_derivation") == 0) {
@@ -4954,6 +4956,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->mld_indicate_disabled = atoi(pos);
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211BN
+ } else if (os_strcmp(buf, "ieee80211bn") == 0) {
+ conf->ieee80211bn = atoi(pos);
+ } else if (os_strcmp(buf, "require_uhr") == 0) {
+ conf->require_uhr = atoi(pos);
+#endif /* CONFIG_IEEE80211BN */
} else if (os_strcmp(buf, "i2r_lmr_policy") == 0) {
conf->i2r_lmr_policy = atoi(pos);
} else {
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 2302e2abe2d8..09e4dbbb0a53 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1114,6 +1114,19 @@ wmm_ac_vo_acm=0
# will be used as the AP MLD MAC address.
#mld_addr=02:03:04:05:06:07
+##### IEEE 802.11bn related configuration #####################################
+
+#ieee80211bn: Whether IEEE 802.11bn (UHR) is enabled
+# 0 = disabled (default)
+# 1 = enabled
+#ieee80211bn=1
+
+#disable_11bn: Boolean (0/1) to disable UHR for a specific BSS
+#disable_11bn=0
+
+# Require stations to support UHR PHY (reject association if they do not)
+#require_uhr=0
+
##### IEEE 802.1X-2004 related configuration ##################################
# Require IEEE 802.1X authorization
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index db5f75ef00d7..2456e8e875f1 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -563,6 +563,7 @@ struct hostapd_bss_config {
bool disable_11ac;
bool disable_11ax;
bool disable_11be;
+ bool disable_11bn;
/* IEEE 802.11v */
int time_advertisement;
@@ -1224,6 +1225,11 @@ struct hostapd_config {
u8 eht_bw320_offset;
#endif /* CONFIG_IEEE80211BE */
+ int ieee80211bn;
+#ifdef CONFIG_IEEE80211BN
+ bool require_uhr;
+#endif
+
/* EHT enable/disable config from CHAN_SWITCH */
#define CH_SWITCH_EHT_ENABLED BIT(0)
#define CH_SWITCH_EHT_DISABLED BIT(1)
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 7a5c5a492ec9..ae87ae7fb9ff 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -470,6 +470,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
size_t he_capab_len,
const struct ieee80211_eht_capabilities *eht_capab,
size_t eht_capab_len,
+ const struct ieee80211_uhr_capabilities *uhr_capab,
+ size_t uhr_capab_len,
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set, const u8 *link_addr, bool mld_link_sta,
@@ -495,6 +497,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
params.he_capab_len = he_capab_len;
params.eht_capab = eht_capab;
params.eht_capab_len = eht_capab_len;
+ params.uhr_capab = uhr_capab;
+ params.uhr_capab_len = uhr_capab_len;
params.he_6ghz_capab = he_6ghz_capab;
params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
params.vht_opmode = vht_opmode;
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 517c16e35c08..fa330e6a094a 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -47,6 +47,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
size_t he_capab_len,
const struct ieee80211_eht_capabilities *eht_capab,
size_t eht_capab_len,
+ const struct ieee80211_uhr_capabilities *uhr_capab,
+ size_t uhr_capab_len,
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set, const u8 *link_addr, bool mld_link_sta,
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 6cfaf099e5e1..ed9c14d4c74e 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -961,6 +961,13 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
true, false);
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211BN
+ if (hapd->iconf->ieee80211bn && !hapd->conf->disable_11bn) {
+ pos = hostapd_eid_uhr_capab(hapd, pos, IEEE80211_MODE_AP);
+ pos = hostapd_eid_uhr_operation(hapd, pos, false);
+ }
+#endif /* CONFIG_IEEE80211BN */
+
#ifdef CONFIG_IEEE80211AC
if (hapd->conf->vendor_vht)
pos = hostapd_eid_vendor_vht(hapd, pos);
@@ -2246,6 +2253,11 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
}
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211BN
+ if (hapd->iconf->ieee80211bn & !hapd->conf->disable_11bn)
+ tail_len += 3 + sizeof(struct ieee80211_uhr_operation);
+#endif /* CONFIG_IEEE80211BN */
+
if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
hapd == hostapd_mbssid_get_tx_bss(hapd))
tail_len += 5; /* Multiple BSSID Configuration element */
@@ -2429,6 +2441,11 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
}
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211bn && !hapd->conf->disable_11bn)
+ tailpos = hostapd_eid_uhr_operation(hapd, tailpos, true);
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_IEEE80211AC
if (hapd->conf->vendor_vht)
tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 799aff76597c..9fdc0a47ef8a 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -404,6 +404,20 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
}
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211BN
+ if ((sta->flags & WLAN_STA_UHR) && sta->uhr_capab) {
+ res = os_snprintf(buf + len, buflen - len, "uhr_capab=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ len += wpa_snprintf_hex(buf + len, buflen - len,
+ (const u8 *) sta->uhr_capab,
+ sta->uhr_capab_len);
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_IEEE80211BN */
+
#ifdef CONFIG_IEEE80211AC
if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
res = os_snprintf(buf + len, buflen - len,
@@ -884,6 +898,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
"ieee80211ac=%d\n"
"ieee80211ax=%d\n"
"ieee80211be=%d\n"
+ "ieee80211bn=%d\n"
"beacon_int=%u\n"
"dtim_period=%d\n",
iface->conf->channel,
@@ -898,6 +913,8 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
!hapd->conf->disable_11ax,
iface->conf->ieee80211be &&
!hapd->conf->disable_11be,
+ iface->conf->ieee80211bn &&
+ !hapd->conf->disable_11bn,
iface->conf->beacon_int,
hapd->conf->dtim_period);
if (os_snprintf_error(buflen - len, ret))
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index e92faa5276a1..951e8b37ca8f 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -143,6 +143,11 @@ static size_t hostapd_supp_rates(struct hostapd_data *hapd, u8 *buf)
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BN
+ if (hapd->iconf->ieee80211bn && hapd->iconf->require_uhr)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_UHR_PHY;
+#endif /* CONFIG_IEEE80211BN */
+
#ifdef CONFIG_SAE
if ((hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
@@ -4378,6 +4383,24 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
}
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211BN
+ if (hapd->iconf->ieee80211bn && !hapd->conf->disable_11bn) {
+ resp = copy_sta_uhr_capab(hapd, sta, IEEE80211_MODE_AP,
+ elems->uhr_capabilities,
+ elems->uhr_capabilities_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
+ if (hapd->iconf->require_uhr && !(sta->flags & WLAN_STA_UHR)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station does not support mandatory UHR PHY - reject association");
+ /* FIXME - need assignment from spec */
+ return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ }
+ }
+#endif /* CONFIG_IEEE80211BN */
#ifdef CONFIG_P2P
if (elems->p2p && ies && ies_len) {
@@ -4831,6 +4854,10 @@ void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
p = hostapd_eid_eht_operation(hapd, p);
}
}
+ if (hapd->iconf->ieee80211bn && !hapd->conf->disable_11bn) {
+ p = hostapd_eid_uhr_capab(hapd, p, IEEE80211_MODE_AP);
+ p = hostapd_eid_uhr_operation(hapd, p, false);
+ }
p = hostapd_eid_ext_capab(hapd, p, false);
p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
@@ -5100,6 +5127,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
struct ieee80211_vht_capabilities vht_cap;
struct ieee80211_he_capabilities he_cap;
struct ieee80211_eht_capabilities eht_cap;
+ struct ieee80211_uhr_capabilities uhr_cap;
int set = 1;
const u8 *mld_link_addr = NULL;
bool mld_link_sta = false;
@@ -5177,6 +5205,11 @@ static int add_associated_sta(struct hostapd_data *hapd,
hostapd_get_eht_capab(hapd, sta->eht_capab, &eht_cap,
sta->eht_capab_len);
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211BN
+ if (sta->flags & WLAN_STA_UHR)
+ hostapd_get_uhr_capab(hapd, sta->uhr_capab, &uhr_cap,
+ sta->uhr_capab_len);
+#endif /* CONFIG_IEEE80211BN */
/*
* Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
@@ -5192,6 +5225,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
sta->flags & WLAN_STA_EHT ? &eht_cap : NULL,
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->flags | WLAN_STA_ASSOC, sta->qosinfo,
sta->vht_opmode, sta->p2p_ie ? 1 : 0,
@@ -5250,6 +5285,12 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
}
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211BN
+ if (hapd->iconf->ieee80211bn && !hapd->conf->disable_11bn) {
+ buflen += hostapd_eid_uhr_capab_len(hapd, IEEE80211_MODE_AP);
+ buflen += 3 + sizeof(struct ieee80211_uhr_operation);
+ }
+#endif /* CONFIG_IEEE80211BN */
buf = os_zalloc(buflen);
if (!buf) {
@@ -5411,6 +5452,13 @@ rsnxe_done:
}
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211BN
+ if (hapd->iconf->ieee80211bn && !hapd->conf->disable_11bn) {
+ p = hostapd_eid_uhr_capab(hapd, p, IEEE80211_MODE_AP);
+ p = hostapd_eid_uhr_operation(hapd, p, false);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_OWE
if (((hapd->conf->wpa_key_mgmt | hapd->conf->rsn_override_key_mgmt |
hapd->conf->rsn_override_key_mgmt_2) & WPA_KEY_MGMT_OWE) &&
@@ -5867,7 +5915,7 @@ static void handle_assoc(struct hostapd_data *hapd,
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
- "Station tried to associate before authentication (aid=%d flags=0x%x)",
+ "Station tried to associate before authentication (aid=%d flags=0x%llx)",
sta ? sta->aid : -1,
sta ? sta->flags : 0);
send_deauth(hapd, mgmt->sa,
@@ -5963,6 +6011,7 @@ static void handle_assoc(struct hostapd_data *hapd,
reassoc ? LINK_PARSE_REASSOC : LINK_PARSE_ASSOC);
if (resp != WLAN_STATUS_SUCCESS)
goto fail;
+
#ifdef CONFIG_IEEE80211R_AP
if (reassoc && sta->auth_alg == WLAN_AUTH_FT)
omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index a2bd583beda3..e660e5deba14 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -322,4 +322,17 @@ void hostapd_link_reconf_resp_tx_status(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len, int ok);
+size_t hostapd_eid_uhr_capab_len(struct hostapd_data *hapd,
+ enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_uhr_capab(struct hostapd_data *hapd, u8 *eid,
+ enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_uhr_operation(struct hostapd_data *hapd, u8 *eid, bool beacon);
+u16 copy_sta_uhr_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ enum ieee80211_op_mode opmode,
+ const u8 *uhr_capab, size_t uhr_capab_len);
+void hostapd_get_uhr_capab(struct hostapd_data *hapd,
+ const struct ieee80211_uhr_capabilities *src,
+ struct ieee80211_uhr_capabilities *dest,
+ size_t len);
+
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_uhr.c b/src/ap/ieee802_11_uhr.c
new file mode 100644
index 000000000000..6befe208bd28
--- /dev/null
+++ b/src/ap/ieee802_11_uhr.c
@@ -0,0 +1,146 @@
+/*
+ * hostapd / IEEE 802.11bn UHR
+ * Copyright (C) 2025 Intel Corporation
+ *
+ * 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/ocv.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_groups.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "wpa_auth.h"
+#include "ieee802_11.h"
+
+
+size_t hostapd_eid_uhr_capab_len(struct hostapd_data *hapd,
+ enum ieee80211_op_mode opmode)
+{
+ struct hostapd_hw_modes *mode;
+ struct uhr_capabilities *uhr_cap;
+
+ mode = hapd->iface->current_mode;
+ if (!mode)
+ return 0;
+
+ uhr_cap = &mode->uhr_capab[opmode];
+ if (!uhr_cap->uhr_supported)
+ return 0;
+
+ return 6;
+}
+
+
+u8 * hostapd_eid_uhr_capab(struct hostapd_data *hapd, u8 *eid,
+ enum ieee80211_op_mode opmode)
+{
+ struct hostapd_hw_modes *mode;
+ struct uhr_capabilities *uhr_cap;
+ struct ieee80211_uhr_capabilities *cap;
+ u8 *pos = eid, *length_pos;
+
+ mode = hapd->iface->current_mode;
+ if (!mode)
+ return eid;
+
+ uhr_cap = &mode->uhr_capab[opmode];
+ if (!uhr_cap->uhr_supported)
+ return eid;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ length_pos = pos++;
+ *pos++ = WLAN_EID_EXT_UHR_CAPABILITIES;
+
+ cap = (struct ieee80211_uhr_capabilities *) pos;
+ os_memcpy(cap->mac, uhr_cap->mac, sizeof(cap->mac));
+ os_memcpy(cap->phy, uhr_cap->phy, sizeof(cap->phy));
+ pos += sizeof(*cap);
+
+ *length_pos = pos - (eid + 2);
+ return pos;
+}
+
+
+u8 * hostapd_eid_uhr_operation(struct hostapd_data *hapd, u8 *eid, bool beacon)
+{
+ struct ieee80211_uhr_operation *oper;
+ u8 *pos = eid;
+
+ if (!hapd->iface->current_mode)
+ return eid;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(*oper);
+ *pos++ = WLAN_EID_EXT_UHR_OPERATION;
+
+ oper = (void *) pos;
+ oper->oper_params = 0;
+
+ /* TODO: Fill in appropriate UHR-MCS max Nss information */
+ oper->basic_uhr_mcs_nss_set[0] = 0x11;
+ oper->basic_uhr_mcs_nss_set[1] = 0x00;
+ oper->basic_uhr_mcs_nss_set[2] = 0x00;
+ oper->basic_uhr_mcs_nss_set[3] = 0x00;
+
+ return pos + sizeof(*oper);
+}
+
+
+static bool ieee80211_invalid_uhr_cap_size(enum hostapd_hw_mode mode,
+ const u8 *uhr_cap, size_t len)
+{
+ return len < sizeof(struct ieee80211_uhr_capabilities);
+}
+
+
+u16 copy_sta_uhr_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ enum ieee80211_op_mode opmode,
+ const u8 *uhr_capab, size_t uhr_capab_len)
+{
+ struct hostapd_hw_modes *c_mode = hapd->iface->current_mode;
+ enum hostapd_hw_mode mode = c_mode ? c_mode->mode : NUM_HOSTAPD_MODES;
+
+ if (!hapd->iconf->ieee80211bn || hapd->conf->disable_11bn ||
+ !uhr_capab ||
+ ieee80211_invalid_uhr_cap_size(mode, uhr_capab, uhr_capab_len)) {
+ sta->flags &= ~WLAN_STA_UHR;
+ os_free(sta->uhr_capab);
+ sta->uhr_capab = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ os_free(sta->uhr_capab);
+ sta->uhr_capab = os_memdup(uhr_capab, uhr_capab_len);
+ if (!sta->uhr_capab) {
+ sta->uhr_capab_len = 0;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_UHR;
+ sta->uhr_capab_len = uhr_capab_len;
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+void hostapd_get_uhr_capab(struct hostapd_data *hapd,
+ const struct ieee80211_uhr_capabilities *src,
+ struct ieee80211_uhr_capabilities *dest,
+ size_t len)
+{
+ if (!src || !dest)
+ return;
+
+ if (len > sizeof(*dest))
+ len = sizeof(*dest);
+ /* TODO: mask out unsupported features */
+
+ os_memset(dest, 0, sizeof(*dest));
+ os_memcpy(dest, src, len);
+}
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index e594696ae9eb..1abcf43a052c 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -436,6 +436,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
os_free(sta->he_capab);
os_free(sta->he_6ghz_capab);
os_free(sta->eht_capab);
+ os_free(sta->uhr_capab);
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);
@@ -556,7 +557,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
int reason;
int max_inactivity = hapd->conf->ap_max_inactivity;
- wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d",
+ wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%llx timeout_next=%d",
hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags,
sta->timeout_next);
if (sta->timeout_next == STA_REMOVE) {
@@ -1828,13 +1829,13 @@ void ap_sta_clear_assoc_timeout(struct hostapd_data *hapd,
}
-int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
+int ap_sta_flags_txt(unsigned long long flags, char *buf, size_t buflen)
{
int res;
buf[0] = '\0';
res = os_snprintf(buf, buflen,
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
(flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
(flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1855,6 +1856,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
(flags & WLAN_STA_VHT ? "[VHT]" : ""),
(flags & WLAN_STA_HE ? "[HE]" : ""),
(flags & WLAN_STA_EHT ? "[EHT]" : ""),
+ (flags & WLAN_STA_UHR ? "[UHR]" : ""),
(flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""),
(flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
(flags & WLAN_STA_SPP_AMSDU ? "[SPP-A-MSDU]" : ""),
@@ -1976,7 +1978,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, NULL, NULL, 0, NULL, 0, NULL, 0, NULL,
sta->flags, 0, 0, 0, 0,
mld_link_addr, mld_link_sta, eml_cap)) {
hostapd_logger(hapd, sta->addr,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 6ed2930dd24b..8a8375a5cbe0 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -20,35 +20,36 @@
#include "hostapd.h"
/* STA flags */
-#define WLAN_STA_AUTH BIT(0)
-#define WLAN_STA_ASSOC BIT(1)
-#define WLAN_STA_SPP_AMSDU BIT(2)
-#define WLAN_STA_AUTHORIZED BIT(5)
-#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
-#define WLAN_STA_SHORT_PREAMBLE BIT(7)
-#define WLAN_STA_PREAUTH BIT(8)
-#define WLAN_STA_WMM BIT(9)
-#define WLAN_STA_MFP BIT(10)
-#define WLAN_STA_HT BIT(11)
-#define WLAN_STA_WPS BIT(12)
-#define WLAN_STA_MAYBE_WPS BIT(13)
-#define WLAN_STA_WDS BIT(14)
-#define WLAN_STA_ASSOC_REQ_OK BIT(15)
-#define WLAN_STA_WPS2 BIT(16)
-#define WLAN_STA_GAS BIT(17)
-#define WLAN_STA_VHT BIT(18)
-#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
-#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
-#define WLAN_STA_VENDOR_VHT BIT(21)
-#define WLAN_STA_PENDING_FILS_ERP BIT(22)
-#define WLAN_STA_MULTI_AP BIT(23)
-#define WLAN_STA_HE BIT(24)
-#define WLAN_STA_6GHZ BIT(25)
-#define WLAN_STA_PENDING_PASN_FILS_ERP BIT(26)
-#define WLAN_STA_EHT BIT(27)
-#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
-#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
-#define WLAN_STA_NONERP BIT(31)
+#define WLAN_STA_AUTH BIT_ULL(0)
+#define WLAN_STA_ASSOC BIT_ULL(1)
+#define WLAN_STA_SPP_AMSDU BIT_ULL(2)
+#define WLAN_STA_AUTHORIZED BIT_ULL(5)
+#define WLAN_STA_PENDING_POLL BIT_ULL(6) /* pending activity poll not ACKed */
+#define WLAN_STA_SHORT_PREAMBLE BIT_ULL(7)
+#define WLAN_STA_PREAUTH BIT_ULL(8)
+#define WLAN_STA_WMM BIT_ULL(9)
+#define WLAN_STA_MFP BIT_ULL(10)
+#define WLAN_STA_HT BIT_ULL(11)
+#define WLAN_STA_WPS BIT_ULL(12)
+#define WLAN_STA_MAYBE_WPS BIT_ULL(13)
+#define WLAN_STA_WDS BIT_ULL(14)
+#define WLAN_STA_ASSOC_REQ_OK BIT_ULL(15)
+#define WLAN_STA_WPS2 BIT_ULL(16)
+#define WLAN_STA_GAS BIT_ULL(17)
+#define WLAN_STA_VHT BIT_ULL(18)
+#define WLAN_STA_WNM_SLEEP_MODE BIT_ULL(19)
+#define WLAN_STA_VHT_OPMODE_ENABLED BIT_ULL(20)
+#define WLAN_STA_VENDOR_VHT BIT_ULL(21)
+#define WLAN_STA_PENDING_FILS_ERP BIT_ULL(22)
+#define WLAN_STA_MULTI_AP BIT_ULL(23)
+#define WLAN_STA_HE BIT_ULL(24)
+#define WLAN_STA_6GHZ BIT_ULL(25)
+#define WLAN_STA_PENDING_PASN_FILS_ERP BIT_ULL(26)
+#define WLAN_STA_EHT BIT_ULL(27)
+#define WLAN_STA_PENDING_DISASSOC_CB BIT_ULL(29)
+#define WLAN_STA_PENDING_DEAUTH_CB BIT_ULL(30)
+#define WLAN_STA_NONERP BIT_ULL(31)
+#define WLAN_STA_UHR BIT_ULL(32)
/* Maximum number of supported rates (from both Supported Rates and Extended
* Supported Rates IEs). */
@@ -93,7 +94,7 @@ struct sta_info {
struct dl_list ip6addr; /* list head for struct ip6addr */
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
u16 disconnect_reason_code; /* RADIUS server override */
- u32 flags; /* Bitfield of WLAN_STA_* */
+ unsigned long long flags; /* Bitfield of WLAN_STA_* */
u16 capability;
u16 listen_interval; /* or beacon_int for APs */
u8 supported_rates[WLAN_SUPP_RATES_MAX];
@@ -197,6 +198,8 @@ struct sta_info {
struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
struct ieee80211_eht_capabilities *eht_capab;
size_t eht_capab_len;
+ struct ieee80211_uhr_capabilities *uhr_capab;
+ size_t uhr_capab_len;
int sa_query_count; /* number of pending SA Query requests;
* 0 = no SA Query in progress */
@@ -405,7 +408,7 @@ void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
void ap_sta_clear_assoc_timeout(struct hostapd_data *hapd,
struct sta_info *sta);
-int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
+int ap_sta_flags_txt(unsigned long long flags, char *buf, size_t buflen);
void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
struct sta_info *sta,
unsigned timeout);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index ae7a7647a5bb..b8416234dae9 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -420,6 +420,14 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
elems->pasn_encrypted_data = pos;
elems->pasn_encrypted_data_len = elen;
break;
+ case WLAN_EID_EXT_UHR_CAPABILITIES:
+ elems->uhr_capabilities = pos;
+ elems->uhr_capabilities_len = elen;
+ break;
+ case WLAN_EID_EXT_UHR_OPERATION:
+ elems->uhr_operation = pos;
+ elems->uhr_operation_len = elen;
+ break;
default:
if (show_errors) {
wpa_printf(MSG_MSGDUMP,
@@ -983,6 +991,14 @@ void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems,
elems->eht_operation = NULL;
elems->eht_operation_len = 0;
break;
+ case WLAN_EID_EXT_UHR_CAPABILITIES:
+ elems->uhr_capabilities = NULL;
+ elems->uhr_capabilities_len = 0;
+ break;
+ case WLAN_EID_EXT_UHR_OPERATION:
+ elems->uhr_operation = NULL;
+ elems->uhr_operation_len = 0;
+ break;
}
}
}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 5f52b573cf54..6b236394a86a 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -122,6 +122,8 @@ struct ieee802_11_elems {
const u8 *rsnxe_override;
const u8 *rsn_selection;
const u8 *wfa_capab;
+ const u8 *uhr_capabilities;
+ const u8 *uhr_operation;
u8 ssid_len;
u8 supp_rates_len;
@@ -191,6 +193,8 @@ struct ieee802_11_elems {
size_t rsnxe_override_len;
size_t rsn_selection_len;
u8 wfa_capab_len;
+ u8 uhr_capabilities_len;
+ u8 uhr_operation_len;
struct mb_ies_info mb_ies;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 6cf8b146d6e1..0a3f37e73914 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -528,6 +528,8 @@
#define WLAN_EID_EXT_BANDWIDTH_INDICATION 135
#define WLAN_EID_EXT_KNOWN_STA_IDENTIFICATION 136
#define WLAN_EID_EXT_PASN_ENCRYPTED_DATA 140
+#define WLAN_EID_EXT_UHR_OPERATION 151
+#define WLAN_EID_EXT_UHR_CAPABILITIES 152
/* Extended Capabilities field */
#define WLAN_EXT_CAPAB_20_40_COEX 0
@@ -1383,6 +1385,7 @@ struct ieee80211_ampe_ie {
#define HT_OPER_PARAM_PCO_PHASE ((u16) BIT(11))
/* B36..B39 - Reserved */
+#define BSS_MEMBERSHIP_SELECTOR_UHR_PHY 120
#define BSS_MEMBERSHIP_SELECTOR_HE_PHY 122
#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123
#define BSS_MEMBERSHIP_SELECTOR_EPD 124
@@ -3214,6 +3217,26 @@ struct ieee80211_s1g_beacon_compat {
le32 tsf_completion;
} STRUCT_PACKED;
+/* UHR Capabilities element format */
+struct ieee80211_uhr_capabilities {
+ /* UHR MAC Capabilities Information */
+ u8 mac[5];
+ /* UHR PHY Capabilities Information */
+ u8 phy[1];
+} STRUCT_PACKED;
+
+#define UHR_OPER_PARAMS_DPS_ENA 0x0001
+#define UHR_OPER_PARAMS_NPCA_ENA 0x0002
+#define UHR_OPER_PARAMS_DBE_ENA 0x0004
+#define UHR_OPER_PARAMS_PEDCA_ENA 0x0008
+
+/* UHR Operation element format */
+struct ieee80211_uhr_operation {
+ le16 oper_params; /* UHR Operation Parameters: UHR_OPER_* bits */
+ u8 basic_uhr_mcs_nss_set[4];
+ /* FIXME: DPS, NPCA, P-EDCA, DBE */
+} STRUCT_PACKED;
+
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index d943062feb41..b08db15f75aa 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -216,6 +216,13 @@ struct eht_capabilities {
u8 ppet[EHT_PPE_THRESH_CAPAB_LEN];
};
+/* struct uhr_capabilities - IEEE 802.11bn UHR capabilities */
+struct uhr_capabilities {
+ bool uhr_supported;
+ u8 mac[5];
+ u8 phy[1];
+};
+
#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
#define HOSTAPD_MODE_FLAG_HE_INFO_KNOWN BIT(2)
@@ -325,6 +332,11 @@ struct hostapd_hw_modes {
* eht_capab - EHT (IEEE 802.11be) capabilities
*/
struct eht_capabilities eht_capab[IEEE80211_MODE_NUM];
+
+ /**
+ * uhr_capab - UHR (IEEE 802.11bb) capabilities
+ */
+ struct uhr_capabilities uhr_capab[IEEE80211_MODE_NUM];
};
@@ -1399,6 +1411,11 @@ struct wpa_driver_associate_params {
*/
int disable_eht;
+ /**
+ * disable_uhr - Disable UHR for this connection
+ */
+ int disable_uhr;
+
/*
* mld_params - MLD association parameters
*/
@@ -2653,6 +2670,9 @@ struct hostapd_sta_add_params {
s8 mld_link_id;
const u8 *mld_link_addr;
u16 eml_cap;
+
+ const struct ieee80211_uhr_capabilities *uhr_capab;
+ size_t uhr_capab_len;
};
struct mac_address {
@@ -7253,6 +7273,8 @@ int ht_supported(const struct hostapd_hw_modes *mode);
int vht_supported(const struct hostapd_hw_modes *mode);
bool he_supported(const struct hostapd_hw_modes *hw_mode,
enum ieee80211_op_mode op_mode);
+bool uhr_supported(const struct hostapd_hw_modes *hw_mode,
+ enum ieee80211_op_mode op_mode);
struct wowlan_triggers *
wpa_get_wowlan_triggers(const char *wowlan_triggers,
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 045c82967f5a..f381e4c74315 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -204,6 +204,13 @@ bool he_supported(const struct hostapd_hw_modes *hw_mode,
}
+bool uhr_supported(const struct hostapd_hw_modes *hw_mode,
+ enum ieee80211_op_mode op_mode)
+{
+ return hw_mode->uhr_capab[op_mode].uhr_supported;
+}
+
+
static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
int capa_trigger, u8 *param_trigger)
{
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index c5bbe119c9a3..ebd28880fa07 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -5853,6 +5853,14 @@ static int wpa_driver_nl80211_sta_add(void *priv,
goto fail;
}
+ if (params->uhr_capab) {
+ wpa_hexdump(MSG_DEBUG, " * uhr_capab",
+ params->uhr_capab, params->uhr_capab_len);
+ if (nla_put(msg, NL80211_ATTR_UHR_CAPABILITY,
+ params->uhr_capab_len, params->uhr_capab))
+ goto fail;
+ }
+
if (params->ext_capab) {
wpa_hexdump(MSG_DEBUG, " * ext_capab",
params->ext_capab, params->ext_capab_len);
@@ -6697,6 +6705,12 @@ static int nl80211_ht_vht_overrides(struct nl_msg *msg,
return -1;
}
+ if (params->disable_uhr) {
+ wpa_printf(MSG_DEBUG, " * UHR disabled");
+ if (nla_put_flag(msg, NL80211_ATTR_DISABLE_UHR))
+ return -1;
+ }
+
return 0;
}
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 88ee9e724a02..8590caa3d4df 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -1941,6 +1941,7 @@ static void phy_info_iftype_copy(struct hostapd_hw_modes *mode,
size_t len;
struct he_capabilities *he_capab = &mode->he_capab[opmode];
struct eht_capabilities *eht_capab = &mode->eht_capab[opmode];
+ struct uhr_capabilities *uhr_capab = &mode->uhr_capab[opmode];
switch (opmode) {
case IEEE80211_MODE_INFRA:
@@ -2048,6 +2049,24 @@ static void phy_info_iftype_copy(struct hostapd_hw_modes *mode,
nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]),
len);
}
+
+ if (!tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC] ||
+ !tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY])
+ return;
+
+ uhr_capab->uhr_supported = true;
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC] &&
+ nla_len(tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC]) >= sizeof(uhr_capab->mac))
+ os_memcpy(uhr_capab->mac,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC]),
+ sizeof(uhr_capab->mac));
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY] &&
+ nla_len(tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY]) >= sizeof(uhr_capab->phy))
+ os_memcpy(uhr_capab->phy,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY]),
+ sizeof(uhr_capab->phy));
}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 9e4a3e6e1a9a..bbca337a7b2d 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -189,6 +189,8 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd)
C2S(NL80211_CMD_SET_TID_TO_LINK_MAPPING)
C2S(NL80211_CMD_ASSOC_MLO_RECONF)
C2S(NL80211_CMD_EPCS_CFG)
+ C2S(NL80211_CMD_NAN_NEXT_DW_NOTIFICATION)
+ C2S(NL80211_CMD_NAN_CLUSTER_JOINED)
C2S(__NL80211_CMD_AFTER_LAST)
}
#undef C2S
diff --git a/src/utils/common.h b/src/utils/common.h
index d7b3600f27e4..1d5634948849 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -458,6 +458,10 @@ void perror(const char *s);
#define BIT(x) (1U << (x))
#endif
+#ifndef BIT_ULL
+#define BIT_ULL(x) (1ULL << (x))
+#endif
+
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config
index 8d94091f35e8..1b4b156dc190 100644
--- a/tests/hwsim/example-hostapd.config
+++ b/tests/hwsim/example-hostapd.config
@@ -128,3 +128,5 @@ CONFIG_NAN_USD=y
CONFIG_MACSEC=y
CONFIG_DRIVER_MACSEC_LINUX=y
CONFIG_SUITEB192=y
+
+CONFIG_IEEE80211BN=y
diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config
index bbbdfd562c12..4389684cb66d 100644
--- a/tests/hwsim/example-wpa_supplicant.config
+++ b/tests/hwsim/example-wpa_supplicant.config
@@ -173,3 +173,5 @@ CONFIG_IEEE80211BE=y
CONFIG_MACSEC=y
CONFIG_DRIVER_MACSEC_LINUX=y
CONFIG_SUITEB192=y
+
+CONFIG_IEEE80211BN=y
diff --git a/tests/hwsim/test_uhr.py b/tests/hwsim/test_uhr.py
new file mode 100644
index 000000000000..a5dfea0aee35
--- /dev/null
+++ b/tests/hwsim/test_uhr.py
@@ -0,0 +1,145 @@
+# UHR tests
+# Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc.
+# Copyright (C) 2025 Intel Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import subprocess
+import tempfile
+import time
+
+import hostapd
+from utils import *
+from hwsim import HWSimRadio
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+from test_eht import eht_verify_status, traffic_test
+
+def uhr_verify_wifi_version(dev):
+ status = dev.get_status()
+ logger.info("station status: " + str(status))
+
+ if 'wifi_generation' not in status:
+ raise Exception("Missing wifi_generation information")
+ if status['wifi_generation'] != "8":
+ raise Exception("Unexpected wifi_generation value: " + status['wifi_generation'])
+
+def uhr_verify_status(wpas, hapd, is_ht=False, is_vht=False):
+ status = hapd.get_status()
+
+ logger.info("hostapd STATUS: " + str(status))
+ if is_ht and status["ieee80211n"] != "1":
+ raise Exception("Unexpected STATUS ieee80211n value")
+ if is_vht and status["ieee80211ac"] != "1":
+ raise Exception("Unexpected STATUS ieee80211ac value")
+ if status["ieee80211ax"] != "1":
+ raise Exception("Unexpected STATUS ieee80211ax value")
+ if status["ieee80211be"] != "1":
+ raise Exception("Unexpected STATUS ieee80211be value")
+ if status["ieee80211bn"] != "1":
+ raise Exception("Unexpected STATUS ieee80211bn value")
+
+ sta = hapd.get_sta(wpas.own_addr())
+ logger.info("hostapd STA: " + str(sta))
+ if sta['addr'] == 'FAIL':
+ raise Exception("hostapd " + hapd.ifname + " did not have a STA entry for the STA " + wpas.own_addr())
+ if is_ht and "[HT]" not in sta['flags']:
+ raise Exception("Missing STA flag: HT")
+ if is_vht and "[VHT]" not in sta['flags']:
+ raise Exception("Missing STA flag: VHT")
+ if "[HE]" not in sta['flags']:
+ raise Exception("Missing STA flag: HE")
+ if "[EHT]" not in sta['flags']:
+ raise Exception("Missing STA flag: EHT")
+ if "[UHR]" not in sta['flags']:
+ raise Exception("Missing STA flag: UHR")
+
+def test_uhr_open(dev, apdev):
+ """UHR AP with open mode configuration"""
+ params = {
+ "ssid": "uhr",
+ "ieee80211ax": "1",
+ "ieee80211be": "1",
+ "ieee80211bn": "1",
+ "require_uhr": "1",
+ }
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except Exception as e:
+ if isinstance(e, Exception) and \
+ str(e) == "Failed to set hostapd parameter ieee80211bn":
+ raise HwsimSkip("UHR not supported")
+ raise
+ if hapd.get_status_field("ieee80211bn") != "1":
+ raise Exception("AP STATUS did not indicate ieee80211bn=1")
+ dev[0].connect("uhr", key_mgmt="NONE", scan_freq="2412")
+ time.sleep(1)
+ sta = hapd.get_sta(dev[0].own_addr())
+ uhr_verify_status(dev[0], hapd, is_ht=True)
+ status = dev[0].request("STATUS")
+ if "wifi_generation=8" not in status:
+ raise Exception("STA STATUS did not indicate wifi_generation=7")
+
+def uhr_mld_ap_wpa2_params(ssid, passphrase=None, key_mgmt="WPA-PSK-SHA256",
+ mfp="2", pwe=None, beacon_prot="1", bridge=False):
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase,
+ wpa_key_mgmt=key_mgmt, ieee80211w=mfp)
+ params['ieee80211n'] = '1'
+ params['ieee80211ax'] = '1'
+ params['ieee80211be'] = '1'
+ params['ieee80211bn'] = '1'
+ params['channel'] = '1'
+ params['hw_mode'] = 'g'
+ params['group_mgmt_cipher'] = "AES-128-CMAC"
+ params['beacon_prot'] = beacon_prot
+ if bridge:
+ params['bridge'] = 'ap-br0'
+
+ if pwe is not None:
+ params['sae_pwe'] = pwe
+
+ return params
+
+def uhr_mld_enable_ap(iface, link_id, params):
+ hapd = hostapd.add_mld_link(iface, link_id, params)
+ hapd.enable()
+
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=1)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ if "AP-ENABLED" not in ev:
+ raise Exception("AP startup failed")
+
+ return hapd
+
+def run_uhr_mld_sae_single_link(dev, apdev, anti_clogging_token=False):
+ with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface), \
+ HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(wpas_iface)
+ check_sae_capab(wpas)
+
+ passphrase = 'qwertyuiop'
+ ssid = "mld_ap_sae_single_link"
+ params = uhr_mld_ap_wpa2_params(ssid, passphrase, key_mgmt="SAE",
+ mfp="2", pwe='2')
+ if anti_clogging_token:
+ params['sae_anti_clogging_threshold'] = '0'
+
+ hapd0 = uhr_mld_enable_ap(hapd_iface, 0, params)
+
+ wpas.set("sae_pwe", "1")
+ wpas.connect(ssid, sae_password=passphrase, scan_freq="2412",
+ key_mgmt="SAE", ieee80211w="2")
+
+ eht_verify_status(wpas, hapd0, 2412, 20, is_ht=True, mld=True,
+ valid_links=1, active_links=1)
+ uhr_verify_status(wpas, hapd0, is_ht=True)
+ uhr_verify_wifi_version(wpas)
+ traffic_test(wpas, hapd0)
+
+def test_uhr_mld_sae_single_link(dev, apdev):
+ """UHR MLD AP with MLD client SAE H2E connection using single link"""
+ run_uhr_mld_sae_single_link(dev, apdev)
diff --git a/tests/hwsim/vm/inside.sh b/tests/hwsim/vm/inside.sh
index 53d67472cd50..8a131cb4346a 100755
--- a/tests/hwsim/vm/inside.sh
+++ b/tests/hwsim/vm/inside.sh
@@ -109,7 +109,7 @@ ip link set lo up
# create logs mountpoint and mount the logshare
mkdir /tmp/logs
if grep -q rootfstype=hostfs /proc/cmdline; then
- mount -t hostfs none /tmp/logs -o $LOGDIR
+ mount -t hostfs none /tmp/logs -o hostfs=$LOGDIR
else
mount -t 9p -o trans=virtio,rw logshare /tmp/logs
fi
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index cb07e7364352..2f1b4f244070 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -948,6 +948,9 @@ endif
ifdef CONFIG_IEEE80211BE
OBJS += src/ap/ieee802_11_eht.c
endif
+ifdef CONFIG_IEEE80211BN
+OBJS += src/ap/ieee802_11_uhr.c
+endif
ifdef CONFIG_WNM_AP
L_CFLAGS += -DCONFIG_WNM_AP
OBJS += src/ap/wnm_ap.c
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index fd7d7a0bd004..74ff02719352 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -1022,6 +1022,9 @@ endif
ifdef CONFIG_IEEE80211BE
OBJS += ../src/ap/ieee802_11_eht.o
endif
+ifdef CONFIG_IEEE80211BN
+OBJS += ../src/ap/ieee802_11_uhr.o
+endif
ifdef CONFIG_WNM_AP
CFLAGS += -DCONFIG_WNM_AP
OBJS += ../src/ap/wnm_ap.o
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index a0f71cfc6ecc..36600fb9695f 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2759,6 +2759,7 @@ static const struct parse_data ssid_fields[] = {
{ INT_RANGE(transition_disable, 0, 255) },
{ INT_RANGE(sae_pk, 0, 2) },
{ INT_RANGE(disable_eht, 0, 1)},
+ { INT_RANGE(disable_uhr, 0, 1)},
{ INT_RANGE(enable_4addr_mode, 0, 1)},
{ INT_RANGE(max_idle, 0, 65535)},
{ INT_RANGE(ssid_protection, 0, 1)},
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index b1ba03ac5a71..8c95257ba53b 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -990,6 +990,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
INT(disable_he);
#endif /* CONFIG_HE_OVERRIDES */
INT(disable_eht);
+ INT(disable_uhr);
INT(enable_4addr_mode);
INT(max_idle);
INT(ssid_protection);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 530b5e96385b..9df5a4c8e0b9 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -1296,6 +1296,14 @@ struct wpa_ssid {
*/
int disable_eht;
+ /**
+ * disable_uhr - Disable UHR (IEEE 802.11bn) for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_uhr;
+
/**
* enable_4addr_mode - Set 4addr mode after association
* 0 = Do not attempt to set 4addr mode
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index ebcc5d7ee4d2..5fca153369ba 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -2370,12 +2370,14 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
if (wpa_s->connection_set &&
(wpa_s->connection_ht || wpa_s->connection_vht ||
- wpa_s->connection_he || wpa_s->connection_eht)) {
+ wpa_s->connection_he || wpa_s->connection_eht ||
+ wpa_s->connection_uhr)) {
ret = os_snprintf(pos, end - pos,
"wifi_generation=%u\n",
+ wpa_s->connection_uhr ? 8 :
wpa_s->connection_eht ? 7 :
- (wpa_s->connection_he ? 6 :
- (wpa_s->connection_vht ? 5 : 4)));
+ wpa_s->connection_he ? 6 :
+ wpa_s->connection_vht ? 5 : 4);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 0e48a31a22fb..5bd23ec2288c 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1039,6 +1039,17 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
continue;
}
+ if (flagged && ((rate_ie[j] & 0x7f) ==
+ BSS_MEMBERSHIP_SELECTOR_UHR_PHY)) {
+ if (!uhr_supported(mode, IEEE80211_MODE_INFRA)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " hardware does not support UHR PHY");
+ return 0;
+ }
+ continue;
+ }
+
#ifdef CONFIG_SAE
if (flagged && ((rate_ie[j] & 0x7f) ==
BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY)) {
@@ -3528,6 +3539,9 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
resp_elems.he_capabilities;
wpa_s->connection_eht = req_elems.eht_capabilities &&
resp_elems.eht_capabilities;
+ wpa_s->connection_uhr = req_elems.uhr_capabilities &&
+ resp_elems.uhr_capabilities;
+
if (req_elems.rrm_enabled)
wpa_s->rrm.rrm_used = 1;
}
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 47c965bfdc70..277f3394ae61 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -2571,6 +2571,7 @@ mscs_fail:
wpa_supplicant_apply_he_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_HE_OVERRIDES */
wpa_supplicant_apply_eht_overrides(wpa_s, ssid, ¶ms);
+ wpa_supplicant_apply_uhr_overrides(wpa_s, ssid, ¶ms);
#ifdef CONFIG_IEEE80211R
if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies &&
get_ie(wpa_s->sme.ft_ies, wpa_s->sme.ft_ies_len,
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 03180a316ffe..a617020b7cdb 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1497,6 +1497,7 @@ static const char *network_fields[] = {
"disable_he",
#endif /* CONFIG_HE_OVERRIDES */
"disable_eht",
+ "disable_uhr",
"ap_max_inactivity", "dtim_period", "beacon_int",
#ifdef CONFIG_MACSEC
"macsec_policy",
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index d45002fd912f..5b91589e37f9 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -2243,7 +2243,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_USE_EXT_KEY_ID, 0);
}
- /* Mark WMM enabled for any HT/VHT/HE/EHT association to get more
+ /* Mark WMM enabled for any HT/VHT/HE/EHT/UHR association to get more
* appropriate advertisement of the supported number of PTKSA receive
* counters. In theory, this could be based on a driver capability, but
* in practice all cases using WMM support at least eight replay
@@ -2254,7 +2254,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
* is far more likely for any current device to support WMM. */
wmm = wpa_s->connection_set &&
(wpa_s->connection_ht || wpa_s->connection_vht ||
- wpa_s->connection_he || wpa_s->connection_eht);
+ wpa_s->connection_he || wpa_s->connection_eht ||
+ wpa_s->connection_uhr);
if (!wmm && bss)
wmm = !!wpa_bss_get_vendor_ie(bss, WMM_IE_VENDOR_TYPE);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_WMM_ENABLED, wmm);
@@ -4857,6 +4858,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
wpa_supplicant_apply_he_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_HE_OVERRIDES */
wpa_supplicant_apply_eht_overrides(wpa_s, ssid, ¶ms);
+ wpa_supplicant_apply_uhr_overrides(wpa_s, ssid, ¶ms);
#ifdef CONFIG_P2P
/*
@@ -6810,6 +6812,17 @@ void wpa_supplicant_apply_eht_overrides(
}
+void wpa_supplicant_apply_uhr_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params)
+{
+ if (!ssid)
+ return;
+
+ params->disable_uhr = ssid->disable_uhr;
+}
+
+
static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
{
#ifdef PCSC_FUNCS
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index e7675a4abe0a..b7d0362223f7 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -984,6 +984,7 @@ struct wpa_supplicant {
unsigned int connection_vht:1;
unsigned int connection_he:1;
unsigned int connection_eht:1;
+ unsigned int connection_uhr:1;
unsigned int disable_mbo_oce:1;
struct os_reltime last_mac_addr_change;
@@ -1649,6 +1650,9 @@ void wpa_supplicant_apply_he_overrides(
void wpa_supplicant_apply_eht_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params);
+void wpa_supplicant_apply_uhr_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params);
int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
--
2.51.0
More information about the Hostap
mailing list