From 9330dccde57ccfa12221e7677d4f32bb3ba245f3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 26 Sep 2025 00:24:17 +0200 Subject: [PATCH] initial UHR support Add initial UHR support, based on a very "superficial" reading of D1.3 (it's incomplete and not well-specified in quite a few places.) Change-Id: I1dd16bb1ffbcf19c38d2d73626776aca4070fbc1 Reviewed-by: Benjamin Berg Signed-off-by: Johannes Berg --- 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 | 42 +++++++ src/ap/ctrl_iface_ap.c | 16 +++ src/ap/hostapd.h | 6 + src/ap/ieee802_11.c | 53 +++++++- src/ap/ieee802_11.h | 13 ++ src/ap/ieee802_11_uhr.c | 145 ++++++++++++++++++++++ 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 | 36 ++++++ src/drivers/driver.h | 28 +++++ src/drivers/driver_common.c | 6 + src/drivers/driver_nl80211.c | 23 ++++ src/drivers/driver_nl80211_capa.c | 19 +++ tests/hwsim/example-hostapd.config | 2 + tests/hwsim/example-wpa_supplicant.config | 2 + tests/hwsim/test_uhr.py | 145 ++++++++++++++++++++++ wpa_supplicant/Android.mk | 3 + wpa_supplicant/Makefile | 7 ++ 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 | 13 ++ wpa_supplicant/sme.c | 1 + wpa_supplicant/wpa_cli.c | 1 + wpa_supplicant/wpa_supplicant.c | 17 ++- wpa_supplicant/wpa_supplicant_i.h | 4 + 36 files changed, 699 insertions(+), 41 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 042f51280427..69c1edbf9bc4 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 c67f874dac4b..a5bdb28a5f62 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 545683871a15..1d5fa9ee7a8d 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -4876,6 +4876,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) { @@ -4973,6 +4975,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 9e3f9d4d524c..27d5a5a39053 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1125,6 +1125,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 15bee01b5ca2..c3ed1893f8af 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -569,6 +569,7 @@ struct hostapd_bss_config { bool disable_11ac; bool disable_11ax; bool disable_11be; + bool disable_11bn; /* IEEE 802.11v */ int time_advertisement; @@ -1233,6 +1234,11 @@ struct hostapd_config { int require_eht; #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 61b823abe138..7234eb316f98 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -477,6 +477,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, @@ -502,6 +504,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 66913ed0fca7..f09b3ad9a306 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 f16ff1fdcb06..f6ac012787e2 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -805,6 +805,13 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211BE */ +#ifdef CONFIG_IEEE80211BN + if (hostapd_is_uhr_enabled(hapd)) { + buflen += hostapd_eid_uhr_capab_len(hapd, IEEE80211_MODE_AP); + buflen += 3 + IEEE80211_UHR_OPER_MAX_SIZE; + } +#endif /* CONFIG_IEEE80211BN */ + buflen += hostapd_eid_mbssid_len(hapd_probed, WLAN_FC_STYPE_PROBE_RESP, NULL, params->known_bss, @@ -977,6 +984,13 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211BE */ +#ifdef CONFIG_IEEE80211BN + if (hostapd_is_uhr_enabled(hapd)) { + 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); @@ -2295,6 +2309,11 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211BE */ +#ifdef CONFIG_IEEE80211BN + if (hostapd_is_uhr_enabled(hapd)) + 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 */ @@ -2477,6 +2496,25 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211BE */ +#ifdef CONFIG_IEEE80211BN + if (hostapd_is_uhr_enabled(hapd)) { + u8 *uhr_oper; + + tailpos = hostapd_eid_uhr_operation(hapd, tailpos, true); + params->uhr_oper = os_zalloc(3 + IEEE80211_UHR_OPER_MAX_SIZE); + if (!params->uhr_oper) + goto error; + + uhr_oper = hostapd_eid_uhr_operation(hapd, params->uhr_oper, + false); + /* check that it was filled */ + if (uhr_oper == params->uhr_oper) { + os_free(params->uhr_oper); + params->uhr_oper = NULL; + } + } +#endif /* CONFIG_IEEE80211BN */ + #ifdef CONFIG_IEEE80211AC if (hapd->conf->vendor_vht) tailpos = hostapd_eid_vendor_vht(hapd, tailpos); @@ -2683,6 +2721,8 @@ error: os_free(head); os_free(tail); os_free(resp); + os_free(params->uhr_oper); + params->uhr_oper = NULL; return -1; #endif /* CONFIG_SAE || NEED_AP_MLME */ } @@ -2714,6 +2754,8 @@ void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) #endif /* CONFIG_IEEE80211AX */ os_free(params->allowed_freqs); params->allowed_freqs = NULL; + os_free(params->uhr_oper); + params->uhr_oper = NULL; } diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index f2093018b939..57aa05e1dccc 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -405,6 +405,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, @@ -885,6 +899,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, @@ -896,6 +911,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, hostapd_is_vht_enabled(hapd), hostapd_is_he_enabled(hapd), hostapd_is_eht_enabled(hapd), + hostapd_is_uhr_enabled(hapd), iface->conf->beacon_int, hapd->conf->dtim_period); if (os_snprintf_error(buflen - len, ret)) diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index be09bb36c93b..11ef777bcbd8 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -944,4 +944,10 @@ hostapd_is_eht_enabled(struct hostapd_data *hapd) return hapd->iconf->ieee80211be && !hapd->conf->disable_11be; } +static inline bool +hostapd_is_uhr_enabled(struct hostapd_data *hapd) +{ + return hapd->iconf->ieee80211bn && !hapd->conf->disable_11bn; +} + #endif /* HOSTAPD_H */ diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 8a6fa084a7fc..65d220681ac9 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -151,6 +151,11 @@ static size_t hostapd_supp_rates(struct hostapd_data *hapd, u8 *buf) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_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) && @@ -4667,6 +4672,24 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } } #endif /* CONFIG_IEEE80211BE */ +#ifdef CONFIG_IEEE80211BN + if (hostapd_is_uhr_enabled(hapd)) { + 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_EHT_NOT_SUPPORTED; + } + } +#endif /* CONFIG_IEEE80211BN */ #ifdef CONFIG_P2P if (elems->p2p && ies && ies_len) { @@ -5254,6 +5277,13 @@ void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd, } } +#ifdef CONFIG_IEEE80211BN + if (hostapd_is_uhr_enabled(hapd)) { + p = hostapd_eid_uhr_capab(hapd, p, IEEE80211_MODE_AP); + p = hostapd_eid_uhr_operation(hapd, p, false); + } +#endif /* CONFIG_IEEE80211BN */ + p = hostapd_eid_ext_capab(hapd, p, false); p = hostapd_eid_mbo(hapd, p, buf + buflen - p); p = hostapd_eid_wmm(hapd, p); @@ -5525,6 +5555,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, epp_sta = false; @@ -5606,6 +5637,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 @@ -5621,6 +5657,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, @@ -5682,6 +5720,12 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, buflen += hostapd_eid_eht_ml_tid_to_link_map_len(hapd); } #endif /* CONFIG_IEEE80211BE */ +#ifdef CONFIG_IEEE80211BN + if (hostapd_is_uhr_enabled(hapd)) { + buflen += hostapd_eid_uhr_capab_len(hapd, IEEE80211_MODE_AP); + buflen += 3 + IEEE80211_UHR_OPER_MAX_SIZE; + } +#endif /* CONFIG_IEEE80211BN */ buf = os_zalloc(buflen); if (!buf) { @@ -5844,6 +5888,13 @@ rsnxe_done: } #endif /* CONFIG_IEEE80211BE */ +#ifdef CONFIG_IEEE80211BN + if (hostapd_is_uhr_enabled(hapd)) { + p = hostapd_eid_uhr_capab(hapd, p, IEEE80211_MODE_AP); + p = hostapd_eid_uhr_operation(hapd, p, false); + } +#endif /* CONFIG_IEEE80211BN */ + #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) && @@ -6345,7 +6396,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, diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 991377c1c483..82c471e92725 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -328,4 +328,17 @@ void hostapd_link_reconf_resp_tx_status(struct hostapd_data *hapd, size_t hostapd_eid_eht_ml_tid_to_link_map_len(struct hostapd_data *hapd); u8 * hostapd_eid_eht_ml_tid_to_link_map(struct hostapd_data *hapd, u8 *eid); +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..d7b00746bd93 --- /dev/null +++ b/src/ap/ieee802_11_uhr.c @@ -0,0 +1,145 @@ +/* + * 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 (!hostapd_is_uhr_enabled(hapd) || !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 fc16b1a426bd..386cd24b21c6 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -474,6 +474,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); @@ -596,7 +597,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) { @@ -1958,13 +1959,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]" : ""), @@ -1985,6 +1986,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]" : ""), @@ -2109,7 +2111,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, epp_sta)) { hostapd_logger(hapd, sta->addr, diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index a7d6c280167d..4154739bb005 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]; @@ -207,6 +208,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 */ @@ -418,7 +421,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 0fd15603c7b7..9b2e8823a7b8 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -434,6 +434,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, @@ -1008,6 +1016,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 438f70dd57b5..a2cdaf41898e 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -128,6 +128,8 @@ struct ieee802_11_elems { const u8 *rsn_selection; const u8 *wfa_capab; const u8 *proximity_ranging; + const u8 *uhr_capabilities; + const u8 *uhr_operation; u8 ssid_len; u8 supp_rates_len; @@ -198,6 +200,8 @@ struct ieee802_11_elems { size_t rsn_selection_len; u8 wfa_capab_len; size_t proximity_ranging_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 55e74f93ac08..17bd3d8d2946 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -541,6 +541,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 @@ -1412,6 +1414,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_EHT_PHY 121 #define BSS_MEMBERSHIP_SELECTOR_HE_PHY 122 #define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123 @@ -3292,6 +3295,39 @@ 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_PEDCA_ENA 0x0004 +#define UHR_OPER_PARAMS_DBE_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, P-EDCA, DBE */ +} STRUCT_PACKED; + +/* Max size in Draft P802.11bn D1.3 with DPS, NPCA, P-EDCA, DBE */ +#define IEEE80211_UHR_OPER_MAX_SIZE \ + (sizeof(struct ieee80211_uhr_operation) + 4 + 6 + 3 + 3) + +/* IEEE P802.11bn/D1.3, Figure 9-aa4 */ +#define UHR_OPER_PARAMS_NPCA_PRIM_CHAN_OFFS 0x0000000F +#define UHR_OPER_PARAMS_NPCA_NPCA_MIN_DUR_THRESH 0x000000F0 +#define UHR_OPER_PARAMS_NPCA_NPCA_SWITCH_DELAY 0x00003F00 +#define UHR_OPER_PARAMS_NPCA_NPCA_SWITCH_BACK_DELAY 0x000FC000 +#define UHR_OPER_PARAMS_NPCA_INIT_NPCA_QRSC 0x00300000 +#define UHR_OPER_PARAMS_NPCA_MOPLEN_NPCA 0x00400000 +#define UHR_OPER_PARAMS_NPCA_DIS_SUBCH_BITMAP_PRES 0x00800000 + #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h index cefc73942ea6..d437dc7d58c5 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) @@ -326,6 +333,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]; }; @@ -1403,6 +1415,11 @@ struct wpa_driver_associate_params { */ int disable_eht; + /** + * disable_uhr - Disable UHR for this connection + */ + int disable_uhr; + /* * mld_params - MLD association parameters */ @@ -1928,6 +1945,12 @@ struct wpa_driver_ap_params { * sae_password - SAE password for SAE offload */ const char *sae_password; + + /** + * uhr_oper - Full UHR operation (beacon only has abridged data), + * includes the extended element header + */ + u8 *uhr_oper; }; struct wpa_driver_mesh_bss_params { @@ -2702,6 +2725,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 { @@ -7380,6 +7406,8 @@ bool he_supported(const struct hostapd_hw_modes *hw_mode, enum ieee80211_op_mode op_mode); bool eht_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 3653c639e843..97ddaa1670f8 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -220,6 +220,12 @@ bool eht_supported(const struct hostapd_hw_modes *hw_mode, return hw_mode->eht_capab[op_mode].eht_supported; } +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 031e68704dc8..1f56bbf8f963 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -5866,6 +5866,15 @@ static int wpa_driver_nl80211_set_ap(void *priv, goto fail; #endif /* CONFIG_IEEE80211AX */ +#ifdef CONFIG_IEEE80211BN + if (params->uhr_oper && + nla_put(msg, NL80211_ATTR_UHR_OPERATION, + /* nl80211 wants it without the extended element header */ + params->uhr_oper[1] - 1, + params->uhr_oper + 3)) + goto fail; +#endif /* CONFIG_IEEE80211BN */ + #ifdef CONFIG_SAE if (wpa_key_mgmt_sae(params->key_mgmt_suites) && nl80211_put_sae_pwe(msg, params->sae_pwe) < 0) @@ -6138,6 +6147,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); @@ -7027,6 +7044,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 18154e2445a4..5b36982dea92 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -2012,6 +2012,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: @@ -2119,6 +2120,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]) >= (int)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]) >= (int)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/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config index 21e44017fb62..e17285240d75 100644 --- a/tests/hwsim/example-hostapd.config +++ b/tests/hwsim/example-hostapd.config @@ -53,6 +53,8 @@ CONFIG_LIBNL3_ROUTE=y CONFIG_IEEE80211R=y CONFIG_IEEE80211AC=y CONFIG_IEEE80211AX=y +CONFIG_IEEE80211BE=y +CONFIG_IEEE80211BN=y CONFIG_OCV=y diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config index 20f3936294ce..aae5f7aff242 100644 --- a/tests/hwsim/example-wpa_supplicant.config +++ b/tests/hwsim/example-wpa_supplicant.config @@ -175,3 +175,5 @@ CONFIG_NAN=y CONFIG_ENC_ASSOC=y CONFIG_PMKSA_PRIVACY=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/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index b8c7152c5b54..b63ab7a45cf8 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -975,6 +975,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 fbfc7307f93a..c6f3378ca9b5 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -1049,6 +1049,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 @@ -1071,6 +1074,10 @@ OBJS += ../src/eap_server/eap_server_methods.o ifdef CONFIG_IEEE80211AC CFLAGS += -DCONFIG_IEEE80211AC endif +ifdef CONFIG_IEEE80211BN +CONFIG_IEEE80211BE=y +CFLAGS += -DCONFIG_IEEE80211BN +endif ifdef CONFIG_IEEE80211BE CONFIG_IEEE80211AX=y CFLAGS += -DCONFIG_IEEE80211BE diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 20dd4eaf3c9e..65a26bc4b719 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -2835,6 +2835,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 2f62e6cee167..e54b2dd4b598 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -1019,6 +1019,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 43a13053090d..b1083fbacf75 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -1300,6 +1300,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 f2125b0b44fc..727d2573a053 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -2379,12 +2379,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 c241ea2cf827..75fca81f3e91 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1063,6 +1063,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)) { @@ -3555,6 +3566,8 @@ static void wpas_parse_connection_info(struct wpa_supplicant *wpa_s, resp_elems.eht_capabilities; if (req_elems.rrm_enabled) wpa_s->rrm.rrm_used = 1; + wpa_s->connection_uhr = req_elems.uhr_capabilities && + resp_elems.uhr_capabilities; #ifdef CONFIG_PMKSA_PRIVACY if (wpa_s->assoc_resp_encrypted && resp_elems.nonce) { diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 8c6bc23e644d..005b498eebc4 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -2984,6 +2984,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 098ada6ceb6f..7542586e3caf 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 937a26265e5b..d0756ade8e64 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -2258,7 +2258,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 @@ -2269,7 +2269,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); @@ -4930,6 +4931,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 /* @@ -6885,6 +6887,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 4148e94ea06d..175a043b0b3a 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1011,6 +1011,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; u8 connection_max_nss_rx; u8 connection_max_nss_tx; @@ -1710,6 +1711,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.53.0